<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
	<channel>
		<title>Armanc Keser</title>
		<description>Personal blog and portfolio of Armanc Keser</description>
		<link>https://armanckeser.com</link>
		<language>en-us</language>
		<atom:link href="https://armanckeser.com/rss.xml" rel="self" type="application/rss+xml"/>
		<lastBuildDate>Sun, 27 Sep 2026 00:00:00 GMT</lastBuildDate>
		<item>
			<title>Restarting fixes Windows because your apps are broken</title>
			<description>Getting a game from 2 FPS back to 60 showed me that Windows apps are broken. Lenovo&apos;s audio &quot;enhancer&quot;, NVIDIA&apos;s overlay and Razer&apos;s mouse software were slowly breaking my laptop, and yours may have them too.</description>
			<content:encoded><![CDATA[<p><em><a href="https://armanckeser.com/writing/restarting-fixes-windows">Interactive figure: open the post to see it.</a></em></p>
<p>For years my computers had tons of problems I didn't understand. Games would stutter, things would
lag, I just didn't have any idea what was going on, and if I restarted, magically sometimes things
would be fixed. This time I couldn't restart, so Opus 5.5 and I had to find the actual problems: my
wife's movie was being transcoded on the GPU, an NVIDIA Overlay process was sitting on 12.7 GB of
memory, and two Razer services had left 32,709 zombie processes behind. Fixing those took Subnautica
2 from 2 FPS back to 60.</p>
<h2>A server that sometimes plays games</h2>
<p>I started self hosting because of Immich, and I don't have a server with a good GPU, so my laptop
(a Legion 5 with a 3070) became the server. These days the same laptop also runs Jellyfin, sits on
top of the TV unit with a gajillion wires going into the back, and never gets turned off.</p>
<p>After a couple days of being on, I realized I was getting really bad performance. This was back when
I was using Cursor, and with Cursor I figured out that Nahimic (the audio "enhancer" Lenovo
preinstalls) was leaking memory through <code>audiodg.exe</code>, and an <code>nssm.exe</code> was leaking too (I never
found out which service registered that one). I removed both from the auto start lists, and later on
disabled Nahimic completely:</p>
<pre><code class="language-powershell">Set-Service NahimicService -StartupType Disabled
</code></pre>
<p>If you want to check your own computer for leaks like these, sort your processes by memory and see
if anything has gotten way bigger than makes sense, especially a process that has been running for
days:</p>
<pre><code class="language-powershell">Get-Process | Sort-Object PM -Descending |
  Select-Object -First 5 Name, StartTime, @{ n = 'GB'; e = { [math]::Round($_.PM / 1GB, 1) } }
</code></pre>
<h2>Air</h2>
<p>Most things were good after that. Then I wanted to use the laptop for gaming with friends and
realized things were still not the best, so I restarted (of course), 3D printed a riser for airflow,
since the Legion pulls air in from underneath, and cleaned the fins with an air gun. That did
improve things, I just couldn't tell you by how much, since I never thought of measuring any of this.</p>
<h2>nvidia-smi will tell you why your GPU is slow</h2>
<p>I wish I had known this ten years ago: <code>nvidia-smi</code> can tell you whether your GPU is being throttled
and why.</p>
<pre><code class="language-powershell">nvidia-smi --query-gpu=clocks_event_reasons.active,clocks.gr,temperature.gpu,power.draw --format=csv -l 2
</code></pre>
<p><code>clocks_event_reasons.active</code> is a bitmask. <code>0x4</code> means the GPU hit its power limit (pretty normal
on a laptop), and <code>0x20</code> means the GPU is too hot and is slowing itself down. I had no idea any of
this existed, I just asked Claude Code why even a simple game like Peak was running badly, and
Claude wrote a logger around that command and had me play for nine minutes:</p>
<pre><code>300 sec  SW POWER CAP           pinned against the 115 W ceiling
228 sec  SW THERMAL SLOWDOWN    too hot to use even that
  0 sec  unthrottled

15:19:46  load starts        65 °C
15:25:53  thermal throttle   87 °C
</code></pre>
<p>Six minutes from cold to thermal throttling, the GPU clock dropping from 1,965 MHz to as low as
1,290, and not a single second where the GPU was running unthrottled. Claude's first guess was dust
or old thermal paste, but Peak was rendering at native 4K, because the laptop is plugged into a 4K
TV and I never changed the resolution. A 115 W laptop GPU can't do 4K, so I dropped the resolution
to 1440p.</p>
<p><em><a href="https://armanckeser.com/writing/restarting-fixes-windows">Interactive figure: open the post to see it.</a></em></p>
<h2>Two modes</h2>
<p>The chart is from later that night, running Subnautica 2 at 1440p. I had never played Subnautica
like that, 60 FPS with maybe a few drops, even though the GPU was thermal throttling more than half
the time. The CPU was the bad part, averaging 98.8 °C. The CPU and GPU share heatpipes in the
Legion, so a hot CPU takes cooling away from the GPU, and since games on this laptop are limited by
the GPU anyway, I capped the CPU at 35 W.</p>
<p>I had Claude create server mode and gaming mode scripts out of everything we learned. Server mode
keeps the laptop quiet and cool and makes sure Jellyfin and friends are running. Gaming mode
switches to the performance power plan, turns on the 15 W GPU boost, sets the fan curve and the CPU
cap, and stops whatever is competing for the disk.</p>
<p>Two things I didn't know before these scripts: <code>powercfg</code> doesn't change the GPU power limit, the
laptop's embedded controller does, so the scripts set the mode there and check that the mode
actually changed. And the power button was glowing red for "performance" while the GPU was still
capped at 115 W out of 130 (<code>nvidia-smi -q -d POWER</code> shows the real limit).</p>
<h2>Twenty days later, 2 FPS</h2>
<p>My friends and I found time to play again, so I pressed the gaming mode shortcut,
opened Subnautica, and... 2 FPS?</p>
<p>Normally I would have just restarted, but my wife was watching a movie on Jellyfin and I couldn't
break her stream. So I told Opus 5.5 that the game was running at 2 FPS and that I couldn't restart,
and sent Opus on a quest to figure out why.</p>
<h2>The movie</h2>
<p>Before I even opened the game the GPU was already at 100 W and 83 °C, the video decoder was 76%
busy, the encoder 43%, and 2.8 GB of the 8 GB of VRAM was taken.</p>
<p>My wife had subtitles on, and the subtitles were PGS. The Jellyfin app on the iPad can't render PGS
(unlike text subtitles like SubRip), so Jellyfin was burning the subtitles into the video, which
means decoding a 4K HDR movie, tone mapping, drawing the subtitles and re-encoding at 1080p.
Subnautica 2 is an Unreal Engine 5 game that wants most of the 8 GB of VRAM, and my best guess for
the 2 FPS is that Subnautica ran out of VRAM and spilled into system memory.</p>
<p>My wife was mostly listening and not really watching, so we turned the subtitles off and Jellyfin
went back to direct playing the file. If you want to check whether something is transcoding on your
GPU:</p>
<pre><code class="language-powershell">nvidia-smi --query-gpu=utilization.encoder,utilization.decoder,memory.used --format=csv -l 2
</code></pre>
<p>The game log says I was getting 21 to 33 FPS after that. We also noticed gaming mode had only half
applied, because Windows had gone back to the Balanced power plan. With both fixed the FPS was okay,
but the game was constantly stuttering and freezing, and last time I played, Subnautica was so
smooth. None of this was making sense.</p>
<h2>The overlay</h2>
<p>Windows had committed 48.6 GB of a 49.9 GB limit, and 12.7 GB of that was a single NVIDIA Overlay
process that had been running for twenty days, since my last restart. (The same process was also
reporting 58 TB of GPU memory on an 8 GB card, so NVIDIA's accounting was clearly broken too.)</p>
<pre><code class="language-powershell">Stop-Process -Id 14080   # NVIDIA Overlay
</code></pre>
<p>NVIDIA starts a new overlay process right away, and the new one uses about 150 MB. Commit went down
to 35.6 GB. I love that we found this one, a restart would have wiped the leak away and I would have
never known the overlay was leaking. The game was still stuttering though.</p>
<h2>Into the kernel</h2>
<p>Then I noticed the stuttering wasn't just the game. Opus's own terminal output was jumping and
stuttering too, so the problem had to be something underneath everything. The System process was
using between 1.1 and 1.4 cores nonstop.</p>
<p>Honestly, this is where I would have given up and restarted. I didn't even know you could get a
stack trace of the Windows kernel, but xperf from the Windows Performance Toolkit does exactly that
(as admin):</p>
<pre><code class="language-powershell">xperf -on PROC_THREAD+LOADER+PROFILE -stackwalk Profile
Start-Sleep 10
xperf -d trace.etl

$env:_NT_SYMBOL_PATH = 'srv*C:\symbols*https://msdl.microsoft.com/download/symbols'
xperf -i trace.etl -symbols -a stack -pid 4 -butterfly 50   # pid 4 is System
</code></pre>
<p><em><a href="https://armanckeser.com/writing/restarting-fixes-windows">Interactive figure: open the post to see it.</a></em></p>
<p>Every window, the game and the terminal included, gets drawn into GPU memory, and the Desktop Window
Manager combines those windows into the frame on your screen. A single kernel thread manages GPU
memory for all programs, and that thread was spending three quarters of its time walking a list.
While that thread is walking the list, everything that needs GPU memory has to wait, so the whole
screen was hitching and not just the game.</p>
<h2>32,709 zombie processes</h2>
<p>A zombie is a process that has exited but can't be cleaned up by Windows, because some other program
still holds a handle to the process. Opus compared the number of process objects in the kernel
against the number of processes that were running:</p>
<pre><code>Live processes: 306   Process objects in kernel: 33,015   => zombies ~ 32,709

Name                    HandleCount
GameManagerService            33695
svchost                       29855
Razer Synapse Service         28193
</code></pre>
<p>Razer Game Manager watches every process you launch to detect games, and seems to open a handle for
every process without ever closing them. Twenty days without a restart comes out to about 1,600 new
zombies a day. The software that makes my mouse glow was slowing down every frame on my screen,
great.</p>
<p>The bar under <code>kernel</code> on the laptop's screen is that list drawn to scale, and the game freezes
whenever the list gets walked.</p>
<pre><code class="language-powershell">Stop-Service 'Razer Game Manager Service', 'Razer Synapse Service' -Force
</code></pre>
<p>Within a minute the zombies went from 32,709 to 260, the System process went from about 1.25 cores
to 0.2, and the stuttering was gone. No restart, and my wife didn't even notice.</p>
<h2>Check yours</h2>
<p>If you are on Windows, open PowerShell and run:</p>
<pre><code class="language-powershell">(Get-Counter '\Objects\Processes').CounterSamples[0].CookedValue - (Get-Process).Count
</code></pre>
<p>A few hundred is normal. Thousands means some program is leaking handles, and this will most likely
show you which one:</p>
<pre><code class="language-powershell">Get-Process | Sort-Object HandleCount -Descending | Select-Object -First 5 Name, HandleCount
</code></pre>
<p>If you run these please tell me your number and your top offender, I am really curious to see what
other people find.</p>
<h2>Everything is broken</h2>
<p>So over the years the problems were Nahimic, some service running under nssm, NVIDIA's overlay and
Razer, programs that either come preinstalled or that pretty much everyone with a gaming laptop has.
Gaming mode now disables the Razer services and prints the zombie count at the end.</p>
<p>Everything is broken on Windows, but at least now I can diagnose the problem and fix it at the
source. "Game has low FPS" used to be a problem I had no idea how to even start on, so I would
restart and hope. If your computer has problems you don't understand, I recommend letting an agent
loose on them, you might be surprised by what the agent finds.</p>]]></content:encoded>
			<link>https://armanckeser.com/writing/restarting-fixes-windows</link>
			<guid isPermaLink="true">https://armanckeser.com/writing/restarting-fixes-windows</guid>
			<pubDate>Sun, 27 Sep 2026 00:00:00 GMT</pubDate>
			<category>windows</category>
			<category>debugging</category>
			<category>gaming</category>
		</item>
		<item>
			<title>Making x.com links open xcancel automatically</title>
			<description>The network-level version needs your own certificate authority, so here is the per-device version instead</description>
			<content:encoded><![CDATA[<h2>Contents</h2>
<h2>Typing cancel into the address bar</h2>
<p>I don't have a Twitter account I use and I don't want the app, but Bluesky never took hold as the place the tech conversation happens, so people I know keep sending me x.com links anyway, from Slack and Signal and everywhere else. My method until now has been to tap the link, go up to the address bar, and add <code>cancel</code> after the <code>x</code>, which turns x.com into <a href="https://xcancel.com">xcancel.com</a> and gives me the tweet with no account and no app. It works fine. Doing it by hand a few times a week for a year is also exactly the sort of small friction that eventually annoys you into automating it, so I sent Claude off on a research mission.</p>
<p>What I wanted was the network-level version, one change on the Pi that already runs AdGuard, fixing every device in the house at once. That turned out to be considerably harder than I expected, and the reason it is hard is interesting enough to write down.</p>
<p>If you have not run into it, xcancel is a fork of <a href="https://github.com/zedeus/nitter">Nitter</a>, the old open-source Twitter frontend. It renders tweets as plain pages, no login, no app prompt, and it takes the same URL paths X does, which is why swapping the domain in the address bar just works.</p>
<h2>The network-level version, and why I gave up on it</h2>
<p>The guides for doing this network-wide all follow the same shape, and <a href="https://gist.github.com/vgmoose/215b6006bcc491891998026768e3872b">vgmoose's gist</a> is the clearest version of it I found. Point your DNS at a box you control, sign a certificate for x.com with your own certificate authority, serve an HTTP redirect to your frontend of choice, then trust that CA on all your devices. The gist has MITM in the title, which is the right word for it.</p>
<p>You cannot skip that last step. DNS maps a name to an address and nothing else, so it can happily send your phone to the Pi instead of to X. But the moment the browser connects it asks the server to prove it is x.com, the Pi cannot, and the connection dies during the TLS handshake before any HTTP request exists. Nothing you run on that box gets a turn. I spent a while thinking about this the wrong way round, imagining some small app on the Pi that would render xcancel in place, and it makes no difference what the app does, because the certificate check happens before the app is ever consulted.</p>
<p>So you either run your own CA or you do not do this at the network layer. And running one means a key sitting on your LAN can impersonate any site to every device that trusts it. That is a big thing to sign up for on the way to reading tweets slightly faster.</p>
<p>Two things rule out the workarounds you might reach for. <code>twitter.com</code> is in the <a href="https://hstspreload.org/">HSTS preload list</a> that ships inside Chrome, and for an HSTS host the browser <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security">does not offer you a way to click through</a> a certificate error at all. <code>x.com</code> is not preloaded, which looks like a loophole until you remember that real x.com sends the HSTS header itself, so any browser that has been there once behaves the same way.</p>
<p>The detail that actually settled it for me is Android specific. Since <a href="https://android-developers.googleblog.com/2016/07/changes-to-trusted-certificate.html">Android 7</a>, apps do not trust user-installed certificate authorities <a href="https://developer.android.com/privacy-and-security/security-config">by default</a>, so a locally signed x.com certificate would satisfy Chrome while getting rejected inside Slack's in-app browser. Covering those in-app browsers is the main thing the network-wide approach is supposed to buy you, and on the platform I care about most it does not buy it.</p>
<h2>Android</h2>
<p>Android turns out to be the good case, because an app there can register as the handler for links and see them before any browser does. Links tapped inside other apps go through it too, which is coverage a browser extension can never give you.</p>
<p><a href="https://github.com/blankdotdev/sidestep">Sidestep</a> is built for precisely this. AGPLv3, free, Android 7 and up, and its manifest declares intent filters for <code>x.com</code>, <code>www.x.com</code>, <code>twitter.com</code> and <code>www.twitter.com</code>, along with a pile of other platforms. It has an instance picker fed from <a href="https://github.com/libredirect/instances">LibRedirect's live instance list</a> and an option to open the rewritten URL immediately instead of showing you a chooser.</p>
<p>Install it through Obtainium pointed at the repo, or from IzzyOnDroid as <code>com.blankdev.sidestep</code>. It is not on Play. The steps below are against version 1.2.5, so expect the labels to drift.</p>
<p>Now the part that actually blocks people. If the X app is installed, go to Settings, Apps, X, Open by default, and turn off "Open supported links". Android 12 and up verifies x.com against a file X publishes at <code>/.well-known/assetlinks.json</code>, and a <a href="https://developer.android.com/training/app-links/verify-android-applinks">verified handler</a> outranks anything you approved by hand, so Sidestep will not get a look at the link until that toggle is off. Uninstalling X does the job too.</p>
<p>After that, set Sidestep as the handler for those domains, turn on the Twitter to Nitter redirect, pick xcancel in the instance picker, and enable immediate navigation.</p>
<p>Test it from Slack or Reddit and not from Chrome. Chrome working proves very little, since Chrome was never the hard case.</p>
<p>If you would rather write the rule yourself than rely on a service list, <a href="https://github.com/TrianguloY/UrlChecker">URLCheck</a> is on F-Droid and its Pattern module does regex rewriting:</p>
<pre><code class="language-json">{
  "X to xcancel": {
    "regex": "^https?://(?:[a-z0-9-]+\\.)?(?:twitter|x)\\.com/(.*)",
    "replacement": "https://xcancel.com/$1",
    "enabled": "true"
  }
}
</code></pre>
<p>It shows a small interstitial before handing off to the browser unless you turn on auto-apply, so it is a bit clunkier than Sidestep. You do know exactly what it will do, though.</p>
<h2>macOS</h2>
<p>Same idea, different mechanism. On a Mac you can make an app the default browser whose only job is to look at each URL and pass it to your real browser, rewritten on the way through. <a href="https://github.com/johnste/finicky">Finicky</a> does this, free and MIT, and it rewrites rather than only picking a browser, which is the distinction that rules out most of the link-chooser apps.</p>
<pre><code class="language-javascript">// ~/.finicky.js
export default {
  defaultBrowser: "Safari",
  rewrite: [
    {
      match: ["x.com/*", "twitter.com/*", "*.x.com/*", "*.twitter.com/*"],
      url: (url) => {
        url.host = "xcancel.com";
        return url;
      },
    },
  ],
};
</code></pre>
<p>In v4 the <code>url</code> function gets a real JavaScript <code>URL</code> object, so you mutate the host and hand it back. The older v3 style that destructured <code>({ url })</code> still runs but logs a legacy warning, which is worth knowing if you copy a config off an old blog post. Same as on Android, this catches links from every app on the machine rather than from one browser.</p>
<p><a href="https://sindresorhus.com/velja">Velja</a> is the paid App Store alternative with an actual GUI, worth a look if a JS config file is not your idea of a good time, though I would confirm its rules can do arbitrary host swapping before paying.</p>
<h2>iOS</h2>
<p>iOS is the bad case, and Apple has shut both doors on purpose. An app can only claim <code>https://</code> links for domains it can <a href="https://developer.apple.com/documentation/xcode/supporting-associated-domains">prove it owns</a>, by hosting a file on them, so nobody but X can claim x.com. The other way in is the <a href="https://developer.apple.com/documentation/xcode/preparing-your-app-to-be-the-default-browser">default browser entitlement</a>, which Apple has to grant you, and a default browser is then not allowed to claim links for specific domains anyway.</p>
<p>What is left is a Safari extension, free for a single rule in the case of Redirect Web for Safari, which covers Safari and not other apps' in-app browsers. Or <a href="https://kagi.com/orion/">Orion</a>, the <a href="https://help.kagi.com/orion/browser-extensions/ios-ipados-extensions.html">first browser</a> to run Chrome and Firefox extensions on iOS and therefore the only way to get <a href="https://libredirect.manerakai.com/">LibRedirect</a> onto a phone, where support is preliminary by Kagi's own description and request rewriting is the flakiest category of extension.</p>
<h2>Windows and Linux</h2>
<p>Both have Finicky equivalents that intercept at the OS level, BrowserTamer and LinkRouter on Windows, <code>handlr</code> or an <code>xdg-open</code> wrapper on Linux. I have not tried any of them, so treat those as search terms and not as a recommendation. The dependable fallback on either is LibRedirect or <a href="https://github.com/einaregilsson/Redirector">Redirector</a> inside Firefox, which only covers links you click in that browser.</p>
<h2>The instances are the fragile part</h2>
<p>Nitter mostly died in early 2024 when X removed guest accounts, which is what the old instances relied on to read anything without logging in. Ars Technica <a href="https://arstechnica.com/tech-policy/2024/02/twitter-front-end-nitter-dies-as-musk-wins-war-against-third-party-services/">covered it at the time</a>, and the <a href="https://github.com/zedeus/nitter/issues/983">issue thread</a> where it got worked out is still there if you want the unhappy version. The instances still standing run on real authenticated accounts, so whoever operates them is cycling banned accounts on your behalf, continuously. Assume whichever one you pick will eventually go dark, and know where the <a href="https://github.com/zedeus/nitter/wiki/Instances">instance list</a> is.</p>
<p>xcancel is the most active survivor and it is in LibRedirect's default instance list. <a href="https://twiiit.com/">twiiit.com</a> is a different thing, a meta-redirector that pings instances every few minutes and forwards you to one that is up, preserving the path, so <code>twiiit.com/user/status/123</code> lands you on the same tweet wherever it sends you. It is not in the default list, so add it as a custom rule if you would rather have round-robin than a fixed instance.</p>
<p>If you pin xcancel and it goes down, you find out by tapping a link and getting nothing, then you go and edit config. With twiiit you mostly just notice that a page took an extra second.</p>]]></content:encoded>
			<link>https://armanckeser.com/writing/redirecting-x-com</link>
			<guid isPermaLink="true">https://armanckeser.com/writing/redirecting-x-com</guid>
			<pubDate>Sat, 25 Jul 2026 00:00:00 GMT</pubDate>
			<category>selfhosted</category>
			<category>privacy</category>
			<category>homelab</category>
		</item>
		<item>
			<title>My PR has been waiting a year, or the exponential curve behind open source backlogs</title>
			<description>What a queuing theory book says about why open source contributions sit for over a year</description>
			<content:encoded><![CDATA[<h2>Contents</h2>
<h2>A year per PR</h2>
<p>I have been trying to get a feature into <a href="https://github.com/jellyfin/jellyfin-web">Jellyfin web</a> for over a year. Opened three PRs, even got <a href="https://github.com/jellyfin/jellyfin-web/pull/6824">two</a> <a href="https://github.com/jellyfin/jellyfin-web/pull/6735">approvals</a>, but zero merges. The feature is relatively isolated and small. When you are adjusting subtitle offset in the player, it adds a timeline that shows you exactly which subtitle will appear when, so you can see what you are doing instead of guessing.</p>
<p><video src="https://github.com/user-attachments/assets/819fee3a-b33d-46ba-95c9-c0687a858a20" controls muted playsinline style="width: 100%; border-radius: 0.75rem; border: 1px solid hsl(var(--accent) / 0.2);"></video></p>
<p>A reviewer gave detailed feedback on the first day. I split the work into <a href="https://github.com/jellyfin/jellyfin-web/pull/6824">smaller</a> <a href="https://github.com/jellyfin/jellyfin-web/pull/6931">pieces</a> when they asked, responded same day, changed the code style to match the project. The <a href="https://github.com/jellyfin/jellyfin-web/pull/6824">refactor PR</a> got two approvals and is just sitting there. The <a href="https://github.com/jellyfin/jellyfin-web/pull/6931">smallest PR</a>, 49 lines added, 0 deleted, has never gotten a single human review.</p>
<p>I brought this up in the Jellyfin community and someone joked that a year is not even that long for Jellyfin PRs. The main maintainer said the backlog "sucks... for everyone" and that every second he is not reviewing makes it worse. I asked if PR size limits, proposal requirements, different processes could help. The answer was mostly "there is only one maintainer."</p>
<p>I think there is more to it than that.</p>
<h2>A common problem</h2>
<p>This is not unusual, most popular open source projects have the same problem.</p>
<p><em><a href="https://armanckeser.com/writing/jellyfin-flow">Interactive figure: open the post to see it.</a></em></p>
<p><a href="https://github.com/python/cpython/pulls">CPython</a> has over 2,200 open PRs. At the <a href="https://pyfound.blogspot.com/2022/05/the-2022-python-language-summit-dealing.html">2022 Python Language Summit</a>, a core developer presented on the backlog specifically and pointed out a chicken-and-egg problem: no active reviewer for a module means no reviews, which means no new reviewers get trained.</p>
<p>Evan You, the creator of <a href="https://vuejs.org/">Vue.js</a>, has talked openly about how the volume of issues became impossible to keep up with as the project grew. A <a href="https://www.sonarsource.com/the-2024-tidelift-maintainer-impact-report.pdf">2024 Tidelift survey</a> found that 60% of open source maintainers have quit or considered quitting. A <a href="https://www.fordfoundation.org/work/learning/research-reports/roads-and-bridges-the-unseen-labor-behind-our-digital-infrastructure/">Ford Foundation report</a> found that the vast majority of open source projects are maintained by one or two people.</p>
<p>Jellyfin is just where I ran into it. And I think the fix is not "find more maintainers." It is changing how work flows through the one maintainer you have.</p>
<h2>Where the time goes</h2>
<p>368 days across my three PRs. The green is days where someone actually reviewed, wrote code, or responded. The rest is waiting.</p>
<p><em><a href="https://armanckeser.com/writing/jellyfin-flow">Interactive figure: open the post to see it.</a></em></p>
<p>The work that happened in those green stretches was good, detailed code reviews, back and forth on architecture, splitting the PR when asked, addressing every ask, but none of it went anywhere because two approvals later the PR is still not merged.</p>
<p>Jellyfin web has about <a href="https://github.com/jellyfin/jellyfin-web/pulls">200 open pull requests</a> and merges roughly 20 to 35 real code PRs a month once you strip out automated dependency updates (which is a lot for a volunteer project). There are about 77 open feature PRs, but features only make up around 21% of what actually gets merged, bug fixes get through but features just sit there.</p>
<h2>The queue math</h2>
<p>Donald Reinertsen's <a href="https://www.amazon.com/Principles-Product-Development-Flow-Generation/dp/1935401009"><em>The Principles of Product Development Flow</em></a> applies <a href="https://en.wikipedia.org/wiki/Queueing_theory">queuing theory</a> to product development, and the core idea is this: when a resource gets close to 100% utilization, queues grow exponentially, not linearly. The <a href="https://en.wikipedia.org/wiki/M/M/1_queue">M/M/1 queue</a> formula is <code>wait = utilization / (1 - utilization)</code>. At 50% utilization the wait is 1x. At 80%, 4x. At 90%, 9x. At 95%, 19x.</p>
<p><em><a href="https://armanckeser.com/writing/jellyfin-flow">Interactive figure: open the post to see it.</a></em></p>
<p>The maintainer said every second not reviewing makes it worse, and that is exactly the trap, because the feeling that slack is wasteful pushes you toward 100% utilization which is where the curve goes vertical, and you can not outwork an exponential.</p>
<p><a href="https://en.wikipedia.org/wiki/Little%27s_law">Little's Law</a> puts a number on it: <code>cycle time = work in progress / throughput</code>. With 200 PRs in the queue and roughly 30 merges a month, the average cycle time works out to 6.7 months. That is not a temporary backlog, that is just what the system does at this load.</p>
<h3>The death spiral</h3>
<p>Large PRs take longer to review, so they pile up, and contributors see the pile and figure they might as well bundle more changes while they wait, which makes PRs larger, which makes reviews slower, which makes the pile bigger. Reinertsen calls this the Batch Size Death Spiral and I think the name is accurate.</p>
<p>Of the 200 open PRs on Jellyfin web, 30 have merge conflicts and 31 are labeled stale. These are PRs that are harder to merge now than when they were first opened, and that only gets worse with time, it does not fix itself.</p>
<h3>Slow feedback wastes everyone's time</h3>
<p>A review gets less useful the longer it takes. At 3 days the contributor still has the context and can turn around changes quickly, at 3 months they have moved on, the codebase has drifted, and rebasing becomes its own project.</p>
<p>A <a href="https://arxiv.org/abs/2110.15447">2021 study</a> looked at over 265,000 pull requests across popular GitHub projects and found that the most common reasons contributors abandoned their PRs were obstacles they ran into and hurdles imposed by maintainers during review, not outright rejection.</p>
<h2>What I think could help</h2>
<p>The <a href="https://en.wikipedia.org/wiki/Theory_of_constraints">Theory of Constraints</a> says: find the bottleneck, squeeze maximum value from it, make everything else serve it. I think all of the following can be done without new maintainers, they are about protecting the time you already have.</p>
<h3>Cap PR size</h3>
<p>A hard limit on changed lines for feature PRs, maybe 300, or at the very least automatic size labels that nudge contributors toward smaller PRs that are each mergeable on their own.</p>
<p>Small PRs are faster to review, carry less risk, and give faster feedback. The project's biggest fear is breaking playback without test coverage, so smaller changes should be an easier sell. The <a href="https://github.com/jellyfin/jellyfin-web/blob/master/CONTRIBUTING.md">contributing guide</a> already says PRs "should represent singular focus." A bot that flags oversized PRs turns that advice into a real constraint. <a href="https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md">Kubernetes does this</a> with automatic size labels so reviewers can pick what fits their time.</p>
<h3>Gate quality before it reaches the bottleneck</h3>
<p>Every minute a maintainer spends on a PR with failing CI, a missing description, or merge conflicts is wasted, and unlike time lost at any other point in the process, time lost at the bottleneck is time lost for the whole system.</p>
<p>Jellyfin already has a <a href="https://github.com/jellyfin/jellyfin-web/blob/master/.github/PULL_REQUEST_TEMPLATE.md">PR template</a> with a checklist and CI that runs linting. The <a href="https://jellyfin.org/docs/general/contributing/llm-policies/">LLM policy</a> filters low-effort AI code. Next step: auto-label PRs "not ready" when CI is red, maybe auto-close PRs with merge conflicts after a set period. <a href="https://github.com/Homebrew/brew">Homebrew</a> leans heavily on automation to <a href="https://docs.brew.sh/Maintainers-Avoiding-Burnout">keep maintainer load sustainable</a>.</p>
<h3>Limit work in progress</h3>
<p>How many PRs can one person really review at a time? Maybe 5, maybe 10, but it is definitely not 200. A hard cap on PRs in active review where you finish or reject before starting new ones would help, because switching between half-reviewed PRs means re-reading context and re-checking what changed, and with 200 open PRs the pull is to skim many, but deep work on a few with forced completion works out better and costs nothing to try.</p>
<h3>Prioritize by value over size</h3>
<p>Not all PRs are worth the same. A security fix has a huge cost of waiting. A cosmetic change has near zero. But there is a less obvious cost here too. If the path of least resistance is to merge refactors and bug fixes because they are smaller and safer, features never land. The application stagnates, contributors stop showing up because they see a project that only does maintenance, and that is its own kind of death spiral.</p>
<p>The system should make it <em>easy</em> for features to get in, not just possible in theory. That means features need a clear path: proposal, size limit, review priority. Reinertsen calls the general principle <a href="https://en.wikipedia.org/wiki/Weighted_shortest_job_first">WSJF</a>, divide the cost of delay by the effort to review, do the highest scoring ones first. But the cost of delay on features is easy to undercount because it is spread across every user who does not get the thing they wanted, and every contributor who does not come back.</p>
<h3>Set a cadence</h3>
<p>A fixed weekly block for PR triage where you go through new ones, finish reviewing in-progress ones, and close stale ones with a kind message. The point is not the schedule itself, it is that things become predictable. Right now there is no way to know when feedback will show up, it could be 3 days or 300, and without that predictability contributors start thinking <em>let me add this other thing while I wait</em>, which is how PRs grow from 50 lines to 500. A weekly cadence turns "first response within 7 days" into something contributors can actually count on.</p>
<p>Even a partial response like "read this, concept looks good, full review in week 3" costs almost nothing and saves the contributor from wondering if their work went into a void.</p>
<h3>Build a reviewer tier</h3>
<p>A few repeat contributors get reviewer status where they can approve but not merge, and the maintainer does a quick check on already-approved PRs which is way faster than a full review from nothing. The <a href="https://github.com/jellyfin/jellyfin-web/blob/master/.github/PULL_REQUEST_TEMPLATE.md">PR checklist</a> already asks contributors to review someone else's PR before their own gets attention, which is the right instinct, and making that structural with real reviewer roles would multiply review capacity.</p>
<h3>Require proposals for features</h3>
<p>The <a href="https://jellyfin.org/docs/general/contributing/development#feature-proposals">docs</a> say features should start with a proposal but it is not enforced, and making it a hard gate would mean feature PRs without an approved proposal do not enter the review queue.</p>
<p>This is the one I feel most personally. I asked multiple times what it would take to get my feature in, what the right approach was, what order to do things in, and I never got an answer. A proposal process would have forced that conversation before I wrote any code, we could have agreed on scope and I would have known what the project actually needed instead of guessing.</p>
<p><a href="https://rust-lang.github.io/rfcs/0002-rfc-process.html">Rust's RFC process</a> is maybe the clearest example. It was <a href="https://github.com/rust-lang/rfcs">created in 2014</a> because features kept landing without upfront alignment, and it let the project scale contribution without scaling the core team at the same rate.</p>
<h2>The actual point</h2>
<p>The conversation I had was stuck on "one maintainer, so PRs take forever" and I just do not buy that. The bottleneck is not that there is one person, it is that 200 PRs of different quality and size all funnel through one point with no flow controls, and the maintainer's time (which is genuinely substantial) gets spent on PRs without descriptions, 1000-line feature PRs, PRs with merge conflicts that have been sitting for months, all demand that produces no output. Protecting that time is a different thing from asking for more of it.</p>
<p>I use Jellyfin every day and I want to contribute to it. Maybe some of these ideas are worth trying, or maybe someone can just tell me how to get the subtitle timeline merged and I will stop talking about queuing theory forever, I would honestly take either one.</p>]]></content:encoded>
			<link>https://armanckeser.com/writing/jellyfin-flow</link>
			<guid isPermaLink="true">https://armanckeser.com/writing/jellyfin-flow</guid>
			<pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate>
			<category>open-source</category>
			<category>engineering</category>
			<category>systems</category>
		</item>
		<item>
			<title>Don&apos;t get used to Skills, they&apos;ll (mostly) be gone in a year.</title>
			<description>Why Skills don&apos;t pass first principles</description>
			<content:encoded><![CDATA[<p>I predict that in a year or so, the way people interact with and think about Skills will be vastly different, in that we will barely know or think about them. Skills are the latest buzz in the world of AI trend catchers, preceded by prompt libraries and multi-agents, and they solve a really important problem, which is progressive disclosure of know-how, letting agents dynamically load context based on the task at hand. But just like their predecessors, they fail some basic first principles in the long run.</p>
<p>Take prompt libraries, or GPTs as they used to be called, why did they fail? No one wants to write prompts, let alone maintain and improve them. We just want the agent we are talking to do the thing we want it to do, the way we want it to do it (preferably even better than what we imagine). Same thing with multi-agents, who wants to sit there thinking about whether a task should spawn a sub-agent or not? I just want the agent I am talking to, to do the task I give it as quickly and as thoroughly as possible.</p>
<p>Skills have the same fundamental problem. If we look at what they get used for, it's a handful of categories: sometimes they are a cache for anything an agent could find if it did some research (best practices for a React framework, how an API works, some source code analysis of a library), many times they hold user preferences on how they would like the agent to act, and most valuably they can capture some repeatable proprietary workflow that holds institutional knowledge (your company's deploy process, the exact sequence of Jira transitions your team decided on). The first two are mostly filling gaps that better models won't have, though there is still taste involved, choosing which best practices you subscribe to among many options isn't something a model can assume on your behalf. But that's exactly the kind of thing a good memory system should learn from working with you. I have Skills for different types of codebases, frontend and backend, and within those how I like the code to be for different languages, and that information fundamentally belongs in memory, not in files I have to manually curate. A model that has worked with me long enough to notice I always rewrite every <code>resp</code> to <code>response</code> and always reject inheritance for code reuse should just <em><strong>learn that</strong></em>, taste and all.</p>
<p>Most of the above use cases will be eaten by agents with better, more proactive memory systems, newer models, and agents that will focus on learning through questions first instead of trying to one-shot every prompt. If we think about where Skills end up from first principles, it comes down to whether the knowledge depends on the external world or on the user. External-world Skills are usually objective facts (even if they can be dynamic), things like how Svelte 5's runes work or what the correct API is for a library that came out after the model's training cutoff. Right now someone has to write these, keep them up to date, and figure out when to load them. In the future these will likely live in central repositories that agents search and pull from on their own, and as models get newer training data and better tool use, many of them will just stop being necessary altogether. User-dependent Skills are opinionated by nature, things like what makes a good PR description, how you like your code structured, what your team's deploy process looks like. Right now you write these yourself, and you maintain them yourself, updating them every time your opinion evolves or your team changes something. In the future these will likely serve as kickstarters that morph over time through interaction, a starting opinion that the agent and user shape together until it reflects what the user actually wants, at which point it's not really a Skill anymore, it's a memory.</p>
<p>You might be hearing about how Skills are the hammer and every problem is a nail, and maybe there is some truth to that. But who actually wants to make hammers at home for every nail they see? If the AI competition keeps forcing Anthropic to innovate at the pace they are, I think it's only a matter of time till they start pulling all necessary Skills to central places and giving their models first-class capabilities to load them, with the user only seeing that they were loaded. The rest will slowly turn to memories, and Skills will be a niche thing that is only kept for backwards compatibility.</p>]]></content:encoded>
			<link>https://armanckeser.com/writing/skills-in-a-year</link>
			<guid isPermaLink="true">https://armanckeser.com/writing/skills-in-a-year</guid>
			<pubDate>Tue, 07 Apr 2026 00:00:00 GMT</pubDate>
			<category>Skills</category>
			<category>agents</category>
		</item>
		<item>
			<title>When will LLMs have their AlphaZero moment?</title>
			<description>Thoughts on the next chapter of LLMs</description>
			<content:encoded><![CDATA[<p>A long time ago, when I first saw the AlphaGo documentary I was blown away by how fast DeepMind went from AlphaGo to AlphaZero. As impressive as AlphaGo was, it was still trained on human played games and it would evaluate itself against whether it thought a professional human would make a play it was thinking of. This did lead to great, actually competitive matches with Lee Sedol, but AlphaGo lost a game. AlphaZero, however, was built from scratch with just the rules of the game and an ability to play itself and learn from it. To get to capacities humans just can't.</p>
<p>With LLMs all being trained on a massive corpus of human written content, and adding the fact that they are literally prediction machines built to please, I can't help but draw parallels between AlphaGo and AlphaZero. I know they are not the same technology, but what would entail "giving the rules of the game" to an LLM to train it? How would one even evaluate performance and be able to retrain based on these results? Is RL after a model is already trained the only way to go or can we train a model to learn to speak and create thoughts by just teaching it the language and its rules as opposed to showing examples?</p>
<p>I am really curious to see if LLMs get their AlphaZero moment, that will be the moment to watch out for.</p>]]></content:encoded>
			<link>https://armanckeser.com/writing/llm-zero</link>
			<guid isPermaLink="true">https://armanckeser.com/writing/llm-zero</guid>
			<pubDate>Wed, 18 Mar 2026 00:00:00 GMT</pubDate>
			<category>LLMs</category>
			<category>AGI</category>
		</item>
		<item>
			<title>LLMs are making me a better engineer</title>
			<description>My experience with LLMs and why I think they are making me a better engineer</description>
			<content:encoded><![CDATA[<blockquote>
<p>TLDR: I find that instead of frustrating myself with a sequence of bad attempts by an LLM, a loop
of reading the output, stopping it immediately as I see something wrong or outside my preference,
and editing my original prompt until I get a consistent and quality output is letting me discover my
requirements in an incremental way, leading to quality code, technical writing, and output in
general. This also sharpens my thought process around
requirements. Additionally, having worked on LLM based systems in the past years, I am finding
myself saying things like "this --buzzword technology-- is a worse --boring but stable
technology--", and deeply thinking about incentives, entropy, and first principles problem solving
when communicating around engineering decisions about LLM based systems.
Altogether, to make useful LLM systems and to make LLMs I use useful, I find myself having to become
a better engineer.</p>
</blockquote>
<h2>Contents</h2>
<h2>The Question</h2>
<p>I know everyone is tired of hearing, talking, thinking, seeing, thinking, and hearing about AI and
LLMs and if they are good or not... so let me add to the pile.</p>
<p>As someone who has paid a yearly subscription fee for Cursor, every time a new article comes out about <a href="https://lucianonooijen.com/blog/why-i-stopped-using-ai-code-editors/">people stopping using AI code editors</a>, <a href="https://aftermath.site/ai-video-game-development-art-vibe-coding-midjourney">AI being forced on workers who don't want it</a>, or <a href="https://www.axios.com/2024/06/13/genai-code-mistakes-copilot-gemini-chatgpt?ref=wheresyoured.at">how bad AI generated code is</a>, I find myself asking the question: Am I using LLMs in a way beneficial to me? And when I say "beneficial", I mean that in the most holistic sense, are they making me get more done in a sustainable way? Are they enabling me to do things I couldn't (or wouldn't)? Are they improving my skills for the long term? And after some consideration, I think the answer is that LLMs are making me (or forcing me to be) a better engineer.</p>
<h2>Why riding the hype wave (carefully) matters</h2>
<p>Let me elaborate. Like many fresh out of the college engineers, I have been actively using and testing any new shiny AI tech that came out (from UI builders to AI native note taking apps to Shopping assistants) in the past 2-3 years. Unlike full-stack or FE engineers who were already tired by the new JS frameworks, or seasoned engineers who already know it'll probably take a life time for a new tech to replace them and just don't care, being a junior backend engineer, I had the capacity and will to keep an eye on trends and new technology no matter how overwhelming (approaching burn out soon).</p>
<p>Which brings me to the first beneficial point of LLMs: they are the cutting edge, and <em><strong>it is
valuable to speak and think cutting edge</strong></em>. Yes, trend catching and switching frameworks and
applications every day is not sustainable or productive and it is tiring, really, really tiring.
When it comes to making a business or just building things, sticking with the tried and true, honing
your skills instead of jumping ship everyday is the way to go. But, I personally see a lot of value
in thinking about how I can use a new technology to solve an old problem, or even better, something
people don't even recognize as a problem. Staying on the cutting edge pushes you to develop the
skills to distinguish <em><strong>what is air and what is impactful</strong></em>. It might be a healthier life to
assume any new thing that will be impactful will find its way to you in time, but most change is
derivative and even the slightest original good idea could shape the future of a new technology, or
present an opportunity you can't find in another time. As an engineer, I find it extremely valuable
that I know what LLMs are actually good or bad at, and even more so that I can have strong opinions
about entire movements (please don't build your system as multi agent). Most valuably, being on the
edge gives you a chance to identify what an industry might have misidentified and a chance to be the
one to fill the gap.</p>
<h2>AI coding removes the friction of starting and finishing projects</h2>
<p>When it comes to using LLMs, I, like most people, started with the chat interfaces and gradually moved up to specialized systems like <a href="https://chef.convex.dev/">chef</a>, <a href="https://vetted.ai/">vetted.ai</a> (been using this since they were called Lustre), and AI coding IDEs like Cursor. Side note: I find it interesting and true to experience that Cursor lists "<em>Cursor lets you breeze through changes by predicting your next edit</em>" as the first feature in their website, as I think that is the part I miss the most when I use a different editor.</p>
<p>It took me quite a while to pull the trigger on Cursor, but I finally bought in for a year on
January. What convinced me was observing how AI coding just made me much more likely to start and
finish projects. I would get an idea like "I want to have an app to make hanging things easier and
why not learn 3D on the web on the way" to creating <a href="https://easy-hang.pages.dev/">EasyHang</a>,
creating a Slackbot for work that automates the process of deploying to staging by getting approvals
from everyone who has commits on the dev branch, or the website you are reading this on. What AI
Coding allowed me to do, was to turn on some lo-fi music, my walking pad, and just keep asking the
AI to fix things my way until things sort of worked. This workflow later got named "vibe coding",
and it gets a bad rep, but the truth is that through vibe coding I got to create applications that I
wouldn't have otherwise. It just made coding for myself, after coding for someone else for an 8 hour
workday, possible and fun.</p>
<h2>The "Vibe Coding" Trap: More Projects, Not Necessarily Better Skills</h2>
<p>Just because AI coders made me code more doesn't mean that they made me a better engineer. And in most of the above cases they didn't. They made me happier, helped me create things that I will be adding to the "things I am proud of" in the <a href="https://yearcompass.com/">year compass</a> I will fill this year (did this section in the last year's compass give me an existential crisis that lead to my yearly subscription to Cursor? We will never know), but I didn't really "learn 3D on the web on the way", I just used it.</p>
<h3>Stop and regenerate over and over again</h3>
<p>This brings me to a revelation I had a few weeks ago using <a href="https://openwebui.com/">OpenWebUI</a> to talk to work allowed LLMs for writing an architectural proposal. I had already written my thoughts in freeform on why and when this architecture would be better, and why it fit our problem space. Unfortunately for me, arguments are better received when they have a certain structure and are accompanied by diagrams and examples, counter examples etc, so I turned to Claude for help.</p>
<p>LLMs are notoriously <a href="https://en.wikipedia.org/wiki/AI_slop">bad at writing</a>, (<a href="https://eqbench.com/creative_writing.html">although maybe they are getting better</a>), so I wasn't expecting much, and in the first iteration I got what I expected. What I didn't expect was how impactful it was for me to stop the LLM's response as soon as I saw something out of order, reword and add new requirements to my question and rerun the generation. Doing this a few turns, I ended up with a proposal with clear structure, short, to the point arguments, specific examples generated based on the context I provided, and helpful diagrams made with <a href="https://mermaid.js.org/">mermaid</a>. It even wrote things like</p>
<blockquote>
<p><strong>The [x] approach doesn't solve the [y] problem—it fragments it across multiple boundaries while adding communication complexity.</strong></p>
</blockquote>
<p>Really understanding the core of my argument and distilling it into a direct point at the end of a
chapter. After a few manual edits, I published the proposal and even got some compliments on the
presentation. Even created a web version by asking the LLM to just make a single page HTML using
prebuilt components from a CDN and vanilla JS. The proposal would not have been close to what it is
if I hadn't spent days writing and editing the free-form version, gathered context, and knew exactly
what I was trying to achieve, but the output of the LLM did teach me things. It showed me how I
could marry all the dispersed ideas I had into a coherent argument, and it wasn't by letting the LLM
do what it wants, but by figuring out <em><strong>what I want</strong></em> through forcing myself to come up with the
requirements that would get the LLM to not generate slop. This might sound like a no-brainer, but I
think the easy thing and what most people do is to just write the next prompt correcting the mistakes the LLM made, that
leading to context overloading and even more mistakes, getting frustrated and concluding that LLMs
are terrible for the job at hand.</p>
<h2>Recognize the artifact, and try to get it right in one prompt (not the first one)</h2>
<p>Nowadays, I use the same principle when coding with Cursor. I try to identify the "artifact" I would
like the AI to generate, and never let it generate a bad output in the first place. Just reword,
clarify, and restart until its 80% of the way there and do the same on improving the artifact
15% more (i.e. letting the LLM edit the first version repeatedly instead of going through
many, many versions and overwhelming the context), and finally manually edit the remaining 5%.</p>
<p>Most LLM applications hope to get some predefined
rules to be enough for the LLM to gather or guess the requirements, but only you know what you want,
and until the LLMs become smarter than you, you have to steer the ship.</p>
<h2>Thinking about LLM systems and trends around them</h2>
<p>There is a separate part of this argument, about how working on LLM systems and tech around them
also pushes me to think about the parallels between some fundamental software problems engineers
have been working on for decades and problems that the industry frames as new to sell useless tech.
And how important of a skill it is to be able to communicate these thoughts in a careful manner when
most people seem to disagree with you. But this probably warrants
a separate blog post.</p>
<h2>So, Are LLMs Making <em>You</em> a Better Engineer?</h2>
<p>Thank you if you have read until this point, I am new to writing a blog so any feedback is more than welcome. What do you think about my argument? Do LLMs make you a better engineer?</p>]]></content:encoded>
			<link>https://armanckeser.com/writing/better-engineer</link>
			<guid isPermaLink="true">https://armanckeser.com/writing/better-engineer</guid>
			<pubDate>Sat, 26 Apr 2025 00:00:00 GMT</pubDate>
			<category>LLMs</category>
			<category>engineering</category>
		</item>
		<item>
			<title>Six Years Later</title>
			<description>A reflection on personal growth and commitments</description>
			<content:encoded><![CDATA[<p>My <a href="https://medium.com/@armanckeser/learning-as-a-part-of-daily-life-52ab1461a9b">latest public blog</a>
was on February of 2019. That is 6 years to the dot. Seems only fitting to start a new blog
reflecting on the past years and on blogging itself.</p>
<p>Firstly, I am glad to have marked my knowledge of the world and of my interests at a point in time
as well as having made it public. The writing pretty much sucks (this will too, probably), and it
makes me sad to not have kept up a consistent blog, but there is a certain charm in capturing your
thoughts (and by extension self) in the past, and also in public.</p>
<p>It is a weird feeling, even though my principles haven't changed much, I have spent a good chunk of
the last 6 years chipping away at problems of the world that bug me the most, challenging my
solution ideas with brilliant people, breaking them (and in turn myself) down and building them back
up again and again. I am proud to have kept my ideals and aspirations through the years while still
striving for learning as much as I can while being open minded. I don't feel like I have changed,
but rather grown, and given that I had completely forgotten its existence before starting to work on
my updated site, reading it was like reading a stranger's promises.</p>
<p>Reading past aspirations or bucket lists can be daunting at times, but this one was a joy (still
cringe at times). Despite having forgotten its existence, maybe having written and promised this to
the world guided me subconsciously in the past 6 years, because I actually seem to have stuck to the
lists I have made and learned a good chunk of the things I have listed:</p>
<p><em><a href="https://armanckeser.com/writing/sixth-year">Interactive figure: open the post to see it.</a></em></p>
<p>I seem to have dropped the ball when it comes to the piano pieces, and this reflection makes me want
to pick it back up (I did learn a lot of Ludovico Einaudi pieces and I even got to see him live!),
but one step at a time.</p>
<h3>What Now?</h3>
<p>This reflection serves as both a checkpoint and a new starting line and betting on that writing goals
down and sharing them publicly might guide me subconsciously in the years to come, I would like to
commit to new things.</p>
<p>My current focus mirrors three core themes from that original list:</p>
<p><em><a href="https://armanckeser.com/writing/sixth-year">Interactive figure: open the post to see it.</a></em></p>
<p>Here's to showing up consistently, embracing the cringe of public learning, and leaving better
breadcrumbs for future me.</p>]]></content:encoded>
			<link>https://armanckeser.com/writing/sixth-year</link>
			<guid isPermaLink="true">https://armanckeser.com/writing/sixth-year</guid>
			<pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate>
			<category>retrospective</category>
			<category>personal-growth</category>
		</item>
	</channel>
</rss>
