Patrick Jackson https://jackson.dev/ Recent content on Patrick Jackson Hugo -- gohugo.io en Tue, 10 Mar 2026 00:00:00 +0000 Feature-First Development https://jackson.dev/post/feature-first-development/ Sun, 14 Dec 2025 00:00:00 +0000 https://jackson.dev/post/feature-first-development/ <p>For my latest <a href="https://eidetica.dev" class="external-link" target="_blank" rel="noopener">quixotic project</a> I decided to develop it in an unstable, &ldquo;feature-first&rdquo; manner. The exact opposite of how I usually develop software.</p> <p>In the past I&rsquo;ve usually tried to write code in a sort of &ldquo;bulletproof&rdquo; style, with every change and feature being over-engineered, over-tested, and fully reviewed before merging to main. Very similar to how <a href="https://tigerbeetle.com/" class="external-link" target="_blank" rel="noopener">TigerBeetle</a> has been publicly working with their <a href="https://github.com/tigerbeetle/tigerbeetle/blob/main/docs/TIGER_STYLE.md" class="external-link" target="_blank" rel="noopener">Tiger Style</a>.</p> <p>I explicitly chose to go a different route in developing Eidetica pre-1.0 and I want to lay out my reasons why.</p> <p>This only applies to the in-development/unstable phase pre-1.0. As I approach and reach 1.0 I&rsquo;ll ramp up my stability expectations to make it into a fully stable, hopefully bulletproof backend.</p> <p><strong>TL;DR - Eidetica will be unstable until the primary features are done, but not a moment longer.</strong></p> <h2 id="eidetica-background"> Eidetica Background <a class="heading-link" href="#eidetica-background"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Eidetica is a decentralized database that is my solution for writing <a href="https://www.inkandswitch.com/local-first/" class="external-link" target="_blank" rel="noopener">local-first software</a>.</p> <p>It&rsquo;s intended to be a &ldquo;batteries-included&rdquo; backend for apps that want to be local-first. Store your data/files inside of the Eidetica data model and it will handle synchronization to all client devices. Users will have their data always synced to their local devices in the background, making both always-on network connections and reliance on central servers unnecessary. And by having user data always stored and editable locally there&rsquo;s no slow-down in the user experience waiting for network roundtrips.</p> <p>It&rsquo;s peer-to-peer, optionally end-to-end encrypted, all that good stuff.</p> <p>It&rsquo;s also currently entirely unstable. I have <a href="https://github.com/arcuru/eidetica?tab=readme-ov-file#experimental" class="external-link" target="_blank" rel="noopener">a big scary note in my README</a> that everything can break without warning and with no migrations supported.</p> <h2 id="why-feature-first"> Why Feature-First <a class="heading-link" href="#why-feature-first"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Honestly it makes me uncomfortable to develop software this way. Working within an unstable codebase feels icky but it has a lot of benefits for early development.</p> <h3 id="1-development-velocity"> 1. Development Velocity <a class="heading-link" href="#1-development-velocity"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>I am building this myself, because I want to get the core designs down first.</p> <p>But that also means there is only 1 developer, with 0 funding except for my own limited &lsquo;fun&rsquo; budget. (<a href="https://jackson.dev/about/" class="external-link" target="_blank" rel="noopener">I&rsquo;m FIRE&rsquo;d</a>, which means I&rsquo;m rich in time but have a limited budget).</p> <p>By holding off on stability as long as possible I can avoid wasting time on that when there are very little/zero actual users.</p> <h3 id="2-chickenegg-problem"> 2. Chicken/Egg Problem <a class="heading-link" href="#2-chickenegg-problem"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Without stability there can&rsquo;t be users. But without users there&rsquo;s no point in being stable.</p> <p>However, there also aren&rsquo;t users without sufficient features. There&rsquo;s no point in paying for stability until you have to.</p> <h3 id="3-dont-know-what-i-dont-know"> 3. Don&rsquo;t Know What I Don&rsquo;t Know <a class="heading-link" href="#3-dont-know-what-i-dont-know"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>It&rsquo;s hard to change core configurations while trying to maintain stability.</p> <p>And the truth is <strong>I don&rsquo;t know the right way to build this</strong>.</p> <p>Sort of.</p> <p>I gave up on trying to design the whole thing correctly from scratch before building it. It&rsquo;s too large to fully model without existing code, and I&rsquo;m trying to avoid making a <em>wrong</em> decision in the core that I will have to support forever.</p> <p>Because of how Eidetica is built it keeps the entire immutable history around, so if there is a mistake in the design it will likely live forever.</p> <p><strong>And I know there are mistakes! I&rsquo;ve already fixed a bunch of them!</strong></p> <p>Instead of trying to figure all those out from the beginning I am building all of my primary hero features before stabilizing. Focusing on the ones where I&rsquo;m unsure how or if they can fit into the existing strategy.</p> <h3 id="4-all-features-are-required"> 4. All Features are Required <a class="heading-link" href="#4-all-features-are-required"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>The subset of Eidetica that needs to be built and stable in order for apps to use it is actually quite large, larger than I thought back when I started. And that number only grows larger when I add in the features on the roadmap that may have an impact on the core.</p> <p>Here&rsquo;s an incomplete list in no particular order:</p> <ul> <li>Built-in CRDTs</li> <li>Bring your own CRDTs</li> <li>Efficient state caching</li> <li>Large Table support</li> <li>Multiple Hash function support</li> <li>Multiple private Key types support</li> <li>P2P synchronization of the Database</li> <li>P2P object storage synchronization</li> <li>Basic Encryption</li> <li>Good Encryption</li> <li>Database Management facilities</li> <li>Background service</li> <li>Local client-service communication</li> <li>History browsing</li> <li>Authentication and Access Management</li> <li>Administration/Moderation</li> <li>Sparse Checkouts</li> <li>Fast Initial Sync</li> <li>Bootstrapping</li> </ul> <p>And probably more I can&rsquo;t think of right now.</p> <p>Some of those are already built, some of those I have solid designs for how they will work and can wait for 1.0, and some of which I&rsquo;m completely unsure of.</p> <p>I&rsquo;m trying to target things that are definitely essential, and then have reasonable plans or ideas of how others can fit into the existing design without breaking it.</p> <h2 id="how-to-stabilize"> How to Stabilize <a class="heading-link" href="#how-to-stabilize"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Since I&rsquo;m building so much unstably I will eventually need to stabilize it. For that I&rsquo;m doing 2 things very intentionally.</p> <h3 id="1-layered-design"> 1. Layered Design <a class="heading-link" href="#1-layered-design"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>The majority of the features are being built on top of Eidetica itself.</p> <ul> <li>Authentication/key management reuses database administration primitives</li> <li>Instance management, user data, and synchronization info are all being stored in Eidetica databases even though they don&rsquo;t need history or synchronization</li> <li>My own features all use my own simple CRDTs</li> <li>Etc.</li> </ul> <p>As much as possible it&rsquo;s being built in this sort of &ldquo;nesting doll&rdquo; style design where each layer builds upon the one below it. As I build and test more features, the lower layers of this stack stabilize with bug fixes and cleanups as I build features on top of them and find more issues.</p> <p>Basically, Eidetica itself is the first consumer of the Eidetica primitives and design as much as possible.</p> <h3 id="2-extensive-testing"> 2. Extensive Testing <a class="heading-link" href="#2-extensive-testing"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>My philosophy is that compute is cheap and dev time is not, and that still applies even though I don&rsquo;t have a salary.</p> <p>That&rsquo;s not to say that I don&rsquo;t care about efficiency in my code, far from it actually, but rather that CI, testing, and other development support things can and should use computing power to save dev time. If the goal is for the final built code to be run at scale then having a few machines constantly making sure it&rsquo;s fully tested and fuzzed should be the default.</p> <p>TigerBeetle, for example, constantly uses <a href="https://mailchi.mp/tigerbeetle/november-2023-in-tigerland" class="external-link" target="_blank" rel="noopener">100 cores</a> just to run their simulation testing infrastructure. Eventually I&rsquo;d like to emulate that for Eidetica.</p> <p>I like having lots of test coverage via unit tests and integration testing, and I plan to also build out fuzz and simulation testing once I approach a more stable base.</p> <h2 id="conclusion"> Conclusion <a class="heading-link" href="#conclusion"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>So for Eidetica it seems to make the most sense to build out this unstable foundation that is underpinned by solid Computer Science theory and research, then transition my efforts into large amounts of testing/stabilizing once the essential features are in place.</p> <p>Instead of trying to determine the exact right path from the beginning, this route of trying to build the whole system in an unstable manner has been working remarkably well.</p> <p>Well, maybe. It&rsquo;s certainly allowed for a very high development velocity right now, and I guess time will tell if it leads to a solid, stable design in the future. I&rsquo;m more comfortable and experienced with the &lsquo;stable&rsquo; development style so I am reasonably confident that that will go fine.</p> <p>So in short: Eidetica will be unstable for as long as is reasonable, but not a moment longer.</p> Sync Conf 2025 Trip Report https://jackson.dev/post/sync-conf-2025/ Fri, 14 Nov 2025 00:00:00 +0000 https://jackson.dev/post/sync-conf-2025/ <p>I just got back from spending 3 days of my time and more than $1000 to travel to and attend a conference. For my &ldquo;side-project&rdquo; <a href="https://eidetica.dev" class="external-link" target="_blank" rel="noopener">Eidetica</a>.</p> <p>What can I say, I&rsquo;m a tech geek with free time and I had some room in my budget.</p> <p>I didn&rsquo;t socialize/connect/chat very much as usual, but that wasn&rsquo;t the point this time. I wanted to see and hear in person from other projects in the space.</p> <p>I had already researched most of the projects on my own, but I did find a few gems that made the trip worthwhile. Additionally, seeing all these things made me more confident in my approach and plans for Eidetica. Most of these &ldquo;sync engines&rdquo; are thinking too small for my liking, or they&rsquo;re trying too hard to productize.</p> <p>If I remember I will come back through and add links to the posted talks in the future.</p> <p>These are my cleaned up notes about some of the talks, you can see the full schedule: <a href="https://schedule.2025.syncconf.dev" class="external-link" target="_blank" rel="noopener">https://schedule.2025.syncconf.dev</a></p> <h3 id="dialogdb"> <a href="https://github.com/dialog-db/dialog-db" class="external-link" target="_blank" rel="noopener">DialogDB</a> <a class="heading-link" href="#dialogdb"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>I think most people tuned out these guys&rsquo; presentation. It was late in the day, they did some weird skit, but what they said was great. I may be biased since their README and docs read almost identically to my own, with many of the same references. From a technical standpoint it is probably the most similar thing that I&rsquo;ve seen to Eidetica.</p> <p>They are using <a href="https://en.wikipedia.org/wiki/Datalog" class="external-link" target="_blank" rel="noopener">Datalog</a> for queries, which I had never heard of before. It appears that it&rsquo;s just their own internal way of handling the data and conflicts. It has nice properties, but it&rsquo;s sort of an unnecessary constraint in this system and I am nearly certain I can implement what they are doing here as a custom Store implementation in Eidetica. Neat to learn about though; I&rsquo;ll have to dig into it more. It does seem to have very good synergies with the decentralized architecture too.</p> <p>Now the most interesting was their use of <a href="https://www.dolthub.com/blog/2025-06-03-people-keep-inventing-prolly-trees/" class="external-link" target="_blank" rel="noopener">Prolly trees</a> to store the <em>Database</em> info. I was already planning to use Prolly trees for storing objects; it enables efficient data sync in instances where there is partial data duplication with other existing objects. IPFS uses it for their object storage and hashing as well.</p> <p>Using a variant of Prolly trees to store the actual Data though is a <em>great</em> idea that I had not thought of in this context. Finding places to apply those principles outside of just object storage is something I&rsquo;ll need to think about some more and add to my toolkit.</p> <h3 id="graft"> <a href="https://graft.rs" class="external-link" target="_blank" rel="noopener">Graft</a> <a class="heading-link" href="#graft"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>I had looked into Graft before, and I liked hearing about it more; however, it fails to meet my bar for decentralization. It&rsquo;s technically interesting, and funny enough, is a backwards way of learning about how SQLite stores data on disk, but the concurrent writes story seems relatively poor.</p> <p>It should work great if you have a single Writer and you want clients to efficiently read from the DB at the edge. It locally hydrates only the needed data from object storage, but my guess is that it struggles in any scenario with writes. It seems to mostly require a global write lock.</p> <h3 id="jazz"> <a href="https://jazz.tools/" class="external-link" target="_blank" rel="noopener">Jazz</a> <a class="heading-link" href="#jazz"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Jazz I had also looked into before, and the talk was on turning their Sync Engine into a Database, which is something I&rsquo;ve been thinking about too. (See my <a href="https://jackson.dev/post/crdts_as_database/" >recent post on CRDTs as databases</a>.)</p> <p>Turns out that their customers had realized it should be closer to a Database before Jazz themselves did and had requested features.</p> <p>The main request seemed to be for some way of having global consistency locks. It seems like users want to, at least temporarily, turn the database into a live <em>distributed</em> database instead of a <em>decentralized</em> database. I&rsquo;m not sure if their requests would be satisfied by the way I have implemented Transactions, but I&rsquo;ve added this idea to my backlog to consider.</p> <p>Actually, I <em>believe</em> that simplifies to a request for Users to be able to invalidate some data themselves, which is a mild problem. In the decentralized system, if you can just write something and say &rsquo;this is a global lock,&rsquo; you can in theory just reject/overwrite any data branches that disagree with that assertion. Planning along those lines are why I set up power levels in my authentication scheme.</p> <p>For the most part though, I&rsquo;ve been operating under the assumption that User data <em>must</em> not fail to merge, and that invalidating entries because of authentication or other reasons would be the sole responsibility of Eidetica. That lets me make certain simplifying assumptions during implementation.</p> <p>Letting User data fail to validly merge is something I&rsquo;d been thinking about though, but I&rsquo;m not going to commit to it yet because there is already a workaround. If Users need to write their own &ldquo;I failed to merge&rdquo; logic into a custom Store (CRDT), then they can also just silently drop the data.</p> <h3 id="ditto"> <a href="https://ditto.live/" class="external-link" target="_blank" rel="noopener">Ditto</a> <a class="heading-link" href="#ditto"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>I don&rsquo;t have much to say about Ditto. I had seen them before, and they&rsquo;re the closest <em>product</em> to what I am building.</p> <p>I think they were also the ones who mentioned having concepts for different <em>types</em> of nodes: servers/sparse clients, etc. This is also in my plans.</p> <p>I do (and already have) recommend them to people for their use case in syncing data in flaky network environments.</p> <h3 id="convex"> <a href="https://www.convex.dev/" class="external-link" target="_blank" rel="noopener">Convex</a> <a class="heading-link" href="#convex"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>They send a deterministic TypeScript (why? why not WASM?) blob to their server to run against the database and return the results. They also track the inputs so they can cache the full query.</p> <p>Eh, this looks like it&rsquo;s just query-based requests. From what I understand, it&rsquo;s similar to GraphQL and is something I&rsquo;d already been thinking about how to do.</p> <p>For my purposes I can&rsquo;t always assume that the remote server has access to the underlying data since it might be E2EE. My plan was to update my Sync code to support a set of requests for which Entries to pull: &ldquo;All children of X,&rdquo; &ldquo;The most recent YY Entries for Z Store,&rdquo; etc. Better requests based on the data <em>may</em> also be something I consider later.</p> <h3 id="notion-offline"> <a href="https://www.notion.so/" class="external-link" target="_blank" rel="noopener">Notion</a> Offline <a class="heading-link" href="#notion-offline"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Notion built their own CRDT to store their block data to allow for offline editing. Makes sense, it&rsquo;s good to know. I enjoyed their talk describing how they did it. Was probably very useful for people who had not seen how CRDTs are built.</p> <h3 id="sqlite-persistence"> <a href="https://sqlite.org/" class="external-link" target="_blank" rel="noopener">SQLite</a> Persistence <a class="heading-link" href="#sqlite-persistence"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>An entire talk about how you can store SQLite objects in a browser client. This is how I planned to implement my WASM build for Eidetica, but I&rsquo;ll learn it from docs.</p> <p>I enjoyed the talk though, and learned a good amount, but I don&rsquo;t have lots of notes.</p> Using CRDTs + Sync as a Database https://jackson.dev/post/crdts_as_database/ Tue, 04 Nov 2025 00:00:00 +0000 https://jackson.dev/post/crdts_as_database/ <p>Can you use a CRDT as a Database?</p> <p>I&rsquo;ve been thinking about this a lot, and my answer is a resounding&hellip;</p> <p>Maybe</p> <p>Depends on your data, and your use case.</p> <p>If your data is a good fit, and you&rsquo;re ready to take on the quirks associated with doing so, I&rsquo;m working on a batteries-included, decentralized DB that will store/track history/sync/query/authenticate/encrypt for you. Choose <a href="https://eidetica.dev" class="external-link" target="_blank" rel="noopener">Eidetica</a> as your database, and get all of that for free.</p> <p>Let me explain.</p> <p>Also to be clear, Eidetica is <a href="https://opensource.org/license/agpl-v3" class="external-link" target="_blank" rel="noopener">AGPLv3 licensed</a> (<a href="https://jackson.dev/post/oss-licensing-sucks/" >see my thoughts on licensing</a>) and I&rsquo;m not ready to accept outside contributions yet but will soon. I will hopefully offer it under a Commercial License in the future but it will always be available under an OSI approved license.</p> <p>Also also, it&rsquo;s proof-of-concept, experimental and no backwards compatibility guaranteed for now. It is definitely pre-Alpha quality. Some of the purported features here <strong>are</strong> mild overstatements of the current capabilities. <strong>If you use it in prod in the near future I will laugh at you.</strong></p> <h2 id="what-is-a-crdt"> What is a CRDT? <a class="heading-link" href="#what-is-a-crdt"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p><a href="https://crdt.tech/" class="external-link" target="_blank" rel="noopener">Conflict-free Replicated Data Type</a> is a type of data structure that you can merge without conflicts. In essence it lets you and I separately create updates to a base set of data, and our changes can be <strong>deterministically</strong> merged together without failure.</p> <p>A write to the CRDT can happen without confirming that it&rsquo;s the only write happening at once. This is the key difference between <strong>distributed</strong> and <strong>decentralized</strong> systems:</p> <ul> <li><strong>Distributed systems</strong> (like traditional distributed databases with Raft or Paxos) require coordination before accepting writes. When you write, the system must confirm with other nodes to maintain consistency, which means you need network connectivity and consensus.</li> <li><strong>Decentralized systems</strong> (using CRDTs) allow each node to accept writes independently without coordination. You can write to your local copy while completely offline, and when nodes eventually reconnect, the CRDT merge algorithm automatically reconciles all changes deterministically.</li> </ul> <p>This &ldquo;write locally, sync later&rdquo; property is what makes CRDTs powerful for local-first applications.</p> <p>It can be as simple as coming up with a deterministic order to the changes and then taking the Last Write Wins (LWW). Technically that results in no conflicts being possible, but there <em>can</em> be logical errors that result. For example, with naive LWW: Device A sets <code>status = &quot;completed&quot;</code> and <code>assigned_to = null</code>, while Device B sets <code>assigned_to = &quot;Bob&quot;</code>. You might end up with a completed task still assigned to someone.</p> <h2 id="simplified-crdt"> Simplified CRDT <a class="heading-link" href="#simplified-crdt"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>For the purposes of using it inside of Eidetica’s syncing framework, a CRDT needs to have the following properties:</p> <ol> <li>Merging in the same order is deterministic and results in identical data.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></li> <li>Merging 2 CRDTs can&rsquo;t fail, it is always valid.</li> </ol> <p>That&rsquo;s all that Eidetica relies on.</p> <h2 id="eidetica-crdts--everything-else"> Eidetica: CRDTs + Everything Else <a class="heading-link" href="#eidetica-crdts--everything-else"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>CRDT libraries like <a href="https://yjs.dev/" class="external-link" target="_blank" rel="noopener">Yjs</a> and <a href="https://automerge.org/" class="external-link" target="_blank" rel="noopener">Automerge</a> provide CRDT implementations but they&rsquo;re only the data format, not complete systems. While there are sync engines written to on top of these like <a href="https://github.com/automerge/automerge-repo" class="external-link" target="_blank" rel="noopener">Automerge Repo</a>, most only work with a single CRDT type.</p> <p>Eidetica is <strong>generic over CRDTs</strong>. You can use the built-in simple CRDTs (like a LWW table or document store), plug in Yjs or Automerge types, or write your own. Eidetica doesn&rsquo;t care what CRDT you use.</p> <p>What Eidetica provides is everything else you need to build a database:</p> <ul> <li><strong>Sync protocol</strong>: Uses <a href="https://www.iroh.computer/" class="external-link" target="_blank" rel="noopener">Iroh</a> for peer-to-peer networking</li> <li><strong>Authentication</strong>: Decentralized identity without a central authority</li> <li><strong>Transactions</strong>: Atomic updates across multiple stores/tables</li> <li><strong>Storage backends</strong>: Persistent storage with history tracking</li> <li><strong>Object storage</strong>: For large binary data (separate from CRDT state)</li> </ul> <p>Think of it as: <strong>CRDT Library + Sync + Auth + Storage + Transactions = Eidetica</strong></p> <h2 id="crdt-as-a-db"> CRDT as a DB <a class="heading-link" href="#crdt-as-a-db"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>CRDTs work well for human-generated data in modest amounts per user. They don&rsquo;t work well for high-volume writes from many nodes.</p> <p>For the data most precious to you - private notes, todo/contacts/calendar, communication, etc - this model should map pretty well. A chat app, for example, can easily be built on top of this without much effort. I&rsquo;ve written a <a href="https://github.com/arcuru/eidetica/tree/main/examples/chat" class="external-link" target="_blank" rel="noopener">small, TUI chat application</a> to demonstrate.</p> <blockquote> <p><strong>Note:</strong> The underlying format in Eidetica is quite similar to <a href="https://matrix.org/" class="external-link" target="_blank" rel="noopener">Matrix</a> and will have similar horizontal scaling problems. Matrix has included a lot of additional niceties for a chat service that make scaling for them even more difficult though.</p> </blockquote> <p>I specifically designed things this way because it&rsquo;s how I want my personal data to be stored. I want my data provably verified, End-to-End encrypted, and available on all my devices even if I&rsquo;m offline. I hate showing up somewhere and having no access to my notes, todo list, rss feed, or chat history because the internet is down. Apps should sync in the background whenever my devices are on.</p> <h2 id="crdt-state-vs-object-storage"> CRDT State vs. Object Storage <a class="heading-link" href="#crdt-state-vs-object-storage"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Not all data should live inside the CRDT itself though.</p> <p>The data stored inside the CRDT type should be relatively small since the system stores the full history and signs each commit. For large or binary data like images, videos, or large documents, it&rsquo;s more efficient to store a hash reference in the CRDT and keep the actual object outside the CRDT state.</p> <p>For example, in a note-taking app:</p> <ul> <li><strong>Store in CRDT</strong>: note title, tags, creation date, description, and the text content.</li> <li><strong>Store as Object</strong>: attachments, images, videos, etc.</li> </ul> <p>This way you can garbage collect and deduplicate objects independently from the CRDT history, which can&rsquo;t easily be modified or pruned. The CRDT gives you the structural data and references, while the object store handles the heavy content.</p> <p>In Eidetica, Object Sync will be a separate storage layer with different configuration options but is not yet implemented.</p> <h2 id="is-this-correct"> Is This Correct? <a class="heading-link" href="#is-this-correct"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Personally I think that a whole lot of data can be modeled as a “CRDT”. I’m taking a bet on that by working on Eidetica to build out exactly the storage/sync system that I personally want to have.</p> <p>This is the type of thing that I think allows for a more <a href="https://www.inkandswitch.com/local-first/" class="external-link" target="_blank" rel="noopener">local-first</a> world. Moving things out of centralized, controlled data and into the hands of users. Each user should own and End-to-End encrypt all of their own data so that <a href="https://www.eff.org/deeplinks/2025/09/chat-control-back-menu-eu-it-still-must-be-stopped-0" class="external-link" target="_blank" rel="noopener">no government can monitor you</a>, and no corp can sell ads off your data unless you let them.</p> <p>And you don’t have to store your data on Google/Meta/Signal’s servers just to have some private chats, or share private data between yourself and your friends. From a purely technical standpoint that is unnecessary. It’s just tricky to get the support and user flows correct, and there’s probably not much money in it.</p> <p>I will fully admit that it does not work for every type of data, but I do think it applies to more areas than you think.</p> <h2 id="is-eidetica-for-you"> Is Eidetica For You? <a class="heading-link" href="#is-eidetica-for-you"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>So my pitch is this: if you are an app developer, and you can store your data in a format where you can guarantee that you can always &ldquo;merge&rdquo; data, and your data storage needs PER USER are relatively modest, then Eidetica can/will provide sync, authentication, encryption (TODO), etc. You don&rsquo;t need a central server, but you can run my sync server yourself to store/sync your user&rsquo;s data. You will eventually be able to sync objects as well (TODO).</p> <p>A user will also be able to run Eidetica locally as a service to store the data from all their local apps using Eidetica and sync in the background.</p> <p>If you choose to store your data in Eidetica, it gives the User full control over their own data, they can choose how much or how little to store locally, store it on their own servers, rent space on someone else&rsquo;s Eidetica service for backups, etc.</p> <p>It is exactly how I want to store my own personal data. I built it for myself first, but I hope it can be useful to others as well.</p> <div class="footnotes" role="doc-endnotes"> <hr> <ol> <li id="fn:1"> <p>Some CRDTs don&rsquo;t care about needing identical order and only need to include the same edits to have a final deterministic state. In effect these are internally managing the state updates and not using a LWW algorithm. From Eidetica&rsquo;s perspective we could eventually reduce the cost of our state computation and let the CRDT structure handle it so we don&rsquo;t do it twice, but for now is an unused property.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p> </li> </ol> </div> I Let AI Vote For Me In The Nix SC Election https://jackson.dev/post/2025-nix-sc-vote/ Sun, 02 Nov 2025 00:00:00 +0000 https://jackson.dev/post/2025-nix-sc-vote/ <blockquote> <p>Discuss and yell at me on Reddit along with the people who didn&rsquo;t have time to read the 1263 words in this post but somehow did have time to read all 188,283 words from the candidates: <a href="https://old.reddit.com/r/NixOS/comments/1onhpbt/i_let_ai_vote_for_me_in_the_nix_sc_election/" class="external-link" target="_blank" rel="noopener">https://old.reddit.com/r/NixOS/comments/1onhpbt/i_let_ai_vote_for_me_in_the_nix_sc_election/</a></p> </blockquote> <blockquote> <p>Update: Something I did not realize until later, there were 24 candidates and approximately 550 eligible voters. More than 4% of all eligible voters were also candidates.</p> </blockquote> <p>I know, kind of a clickbait title. I&rsquo;m sorry.</p> <p>The Nix Steering Committee (SC) election finished this weekend, and I had been putting off voting in it.</p> <p>The SC is a 7 member committee serving 2 year terms that is sort of in charge of the overall Nix project, and there is always drama with them. To be honest, I still don&rsquo;t know all the details of exactly what they do or are in charge of, but I do know a little bit about the drama. Just to get an idea, here&rsquo;s what I remember off the top of my head:</p> <ul> <li>One of them took a job at a weapons manufacturer, which also sponsored the main Nix conference.</li> <li>The moderation team (a separate group) fully resigned over interference from the SC, I think that was last year?</li> <li>Conflicts of Interest are rampant in Nix leadership, which is sort of inevitable with a large OSS project, but Nix is pretty bad.</li> <li>Last month 1 of the members resigned after 1 year of their 2 year term, so 5 of the 7 seats were up for election.</li> </ul> <p>I&rsquo;ve not been following the drama recently as I&rsquo;ve been heads-down working on <a href="https://eidetica.dev" class="external-link" target="_blank" rel="noopener">Eidetica</a>, but I still use Nix for my personal machines and my projects. I still donate to them and contribute to Nixpkgs on occasion, even though I have moved over to using <a href="https://lix.systems" class="external-link" target="_blank" rel="noopener">Lix</a> instead of CppNix.</p> <p>I also have strong feelings about SOME of the issues facing Nix but not all of them, so I really wanted to vote in this election.</p> <h2 id="timing"> Timing <a class="heading-link" href="#timing"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Unfortunately I’ve been busy, so I got a final “last chance to vote” notification while on vacation. I ultimately had about an hour available to do this before needing to head off to dinner.</p> <p>However when I looked at the details I hit a small snag…</p> <h2 id="voting-hell"> Voting Hell <a class="heading-link" href="#voting-hell"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Candidate info was stored in a git repo. Each candidate had their own markdown file where they explained their positions and filled out FAQ’s sent in by the community to the repo. That’s actually a great setup for a code-adjacent election, I like it.</p> <p>So I downloaded the repo to start looking.</p> <h3 id="problem-1-24-candidates"> Problem 1: 24 Candidates <a class="heading-link" href="#problem-1-24-candidates"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>There are 24 candidates who have put themselves up for the committee. I know some of them, and one of them even pinged me on Matrix recently (don&rsquo;t know why, because they need to read <a href="http://nohello.net" class="external-link" target="_blank" rel="noopener">nohello.net</a> and I always assume a &ldquo;hi&rdquo; with no context is spam).</p> <p>Ok, this’ll be fine, I’ll read some of the statements and just vote for the ones I know I agree with.</p> <h3 id="problem-2-fully-ranked-choice-voting"> Problem 2: Fully Ranked Choice Voting <a class="heading-link" href="#problem-2-fully-ranked-choice-voting"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>The voting is ranked choice, which is great! An excellent way to vote for people, especially when there are 5 slots to fill from 24 people.</p> <p>Unfortunately, it forces me to rank ALL 24 CANDIDATES.</p> <p>Well that sucks, but I understand statistics and I&rsquo;m not sure which method of ranked choice they&rsquo;re using, so I don&rsquo;t want to just pick randomly for the remaining candidates because I know that can mess with the rankings (I think? Maybe? I&rsquo;m not 100% on that, but why else would they make us rank EVERYBODY?). So I guess I’m going to look at more of the candidates to try to rank them. How bad is that going to be?</p> <h3 id="problem-3-size"> Problem 3: Size <a class="heading-link" href="#problem-3-size"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>&gt; cat candidates/* | wc -w </span></span><span style="display:flex;"><span>188283 </span></span></code></pre></div><p>Wait&hellip; Is that&hellip; are there 188,000 words in the candidates folder? What the hell?</p> <p>That&rsquo;s&hellip; hold on.</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>&gt; 188283 / 24 </span></span><span style="display:flex;"><span>7845 </span></span></code></pre></div><p>That&rsquo;s nearly 8000 words per candidate. I can&rsquo;t read that right now.</p> <h2 id="timeline"> Timeline <a class="heading-link" href="#timeline"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Now 10 minutes have passed. I skimmed a couple candidate profiles before I realized how the voting worked. From references they each make, I&rsquo;m now starting to get an idea of how much <a href="https://xkcd.com/37/" class="external-link" target="_blank" rel="noopener">random ass-drama</a> there has been in the Nix community lately.</p> <p>(I&rsquo;m not going to go into detail about what the various drama&rsquo;s are in this community, that&rsquo;s for some other time.)</p> <p>But now I have more context, and this is a problem. I really don&rsquo;t want to skip voting just because it&rsquo;s annoying, because I have strong feelings about some of these issues and I enjoy complaining about Nix leadership a lot in private. And as a rule I won&rsquo;t complain about things when I can do something about it and choose not to.</p> <p>But time is running out. And I don&rsquo;t have time to read 200,000 words.</p> <p>So let&rsquo;s use the skills I&rsquo;ve been cultivating with code. I&rsquo;ll do this with an LLM.</p> <h2 id="solving-with-llms"> Solving with LLMs <a class="heading-link" href="#solving-with-llms"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I&rsquo;ve been using these newfangled tools extensively for coding, and conveniently, I already have all this data in markdown files or website links, so I can quickly spin up a workflow for this.</p> <p>Here’s what I did:</p> <ol> <li>I wrote up a list of things I cared about to serve as my value set. I compiled them from skimming several candidate descriptions and explicitly addressing some of the things I cared about. Mainly, these took the form of statements like &ldquo;I want X on this issue, I think Y is a problem, I prefer candidates who Z, etc.&rdquo;</li> <li>Because of the size of candidate files, I batched the reviews for my LLM of choice and had it check 2 at a time.</li> <li>For each batch, I instructed it to summarize the candidates and their views according to my values, and place the resulting summaries into a summary file.</li> <li>Once complete, I had it read that file and my value set again and then rank the candidates.</li> <li>For verification, since I had limited time, I compared them against the ones I had randomly picked and spot checked a few more at various points in the ranking scale. I confirmed the summaries and rankings matched with what I could glean from scanning the candidate statements.</li> </ol> <p>Notice: I DID NOT just tell it to vote for me. I DID NOT trust the output without verification. And I DID understand its context limitations and work around them.</p> <h2 id="would-i-do-it-again"> Would I Do It Again? <a class="heading-link" href="#would-i-do-it-again"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Yes, but only for things I simply don’t have time to fully review myself.</p> <p>If this came up again and I did have more time, I would probably start with this method for an initial ranking, and then use that list as a starting point and adjust it more as I reviewed them myself.</p> <p>Unless I have no other choice, that&rsquo;s how I think LLMs should be used. They&rsquo;re an enhancement to my own capabilities, a &ldquo;bicycle for the mind&rdquo; to be cliche about it. They are extremely fancy autocomplete that can do a limited set of actions on text but can&rsquo;t be fully trusted. Their values can&rsquo;t be trusted, their actions need to be reviewed and verified.</p> <p>I use them only for things I already know how to do; they just help me do it significantly faster.</p> <p>I&rsquo;m sure that many people will have problems with me voting this way, and TBH I don&rsquo;t really care. I&rsquo;m happy to listen to your complaints only if you can seriously claim that you read all 188,283 words from the candidates.</p> OSS Licensing Sucks https://jackson.dev/post/oss-licensing-sucks/ Fri, 05 Sep 2025 00:00:00 +0000 https://jackson.dev/post/oss-licensing-sucks/ <p>I am working on <a href="https://eidetica.dev" class="external-link" target="_blank" rel="noopener">Eidetica</a>, an OSS project that may one day be a good backend for apps to store data. It is primarily useful as an open library for free software to use but may be valuable enough for companies to use and pay for.</p> <p>What !%&amp;*-ing license should it have?</p> <p>I basically want a license that says &ldquo;<em>use it for free, but if you make money you have to pay for it.</em>&rdquo; I don&rsquo;t actually care so much about keeping the source code always available, or forcing all users/forks to share code. Don&rsquo;t get me wrong, those are all good things and I want them to happen, but it&rsquo;s not a requirement.</p> <p>What I do care about is that Amazon can&rsquo;t take it and make hundreds of millions of dollars off it without giving me a penny.</p> <p>The reality is that none of the broadly accepted Open Source licenses satisfy that criteria. I understand that technically that wouldn&rsquo;t be an Open Source license, but the community needs some accepted license that allows software developers to both share code where possible but also keep enough control to make money from it.</p> <p>Lets analyze the potentials, with my requirements, and compare to see which may fit well.</p> <blockquote> <p>Update December &lsquo;25: To keep in mind while reading, my intention now is to use AGPL + Commercial, and require a CLA that assigns copyright to me with the agreement that the code must always be offered under an OSI approved license. This is exactly what <a href="https://element.io/blog/synapse-now-lives-at-github-com-element-hq-synapse/" class="external-link" target="_blank" rel="noopener">Matrix/Synapse/Element does</a>. This will allow me to sell exceptions for funding, while also giving contributors assurance that the code will always remain open source.</p> </blockquote> <h1 id="perspective"> Perspective <a class="heading-link" href="#perspective"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>First we need a lens to view this through. I’m building the following.</p> <ul> <li>A decentralized, Peer-to-Peer Database and integrated Object Store.</li> <li>Packaged as a library so you can embed it in your own application.</li> <li>Written in Rust, with bindings for WASM.</li> <li>Plus a self-hostable Synchronization Node with multi-user support.</li> </ul> <p>Fundamentally it is a database that communicates over the network, offered as a library and a binary. It can be embedded into your app, and can communicate with a hosted Sync server or a users private Sync server to assist with replication.</p> <p>What license should it use? Should the binary be a different license?</p> <p>The main problem is that I can’t both distribute the core of the library so that others can embed it into their OSS applications and <em>also</em> somehow charge for it.</p> <p>The value of this thing is in the protocols, the flexibility/modularity of the codebase, and the interoperability of storing all of a user’s data into the same decentralized system.</p> <p>For example, if a user is using several different applications that all store data in Eidetica, all the user needs to do is run a Sync node on their laptop/desktop/server and all the data from all of those apps will be backed up there. Or pay someone to host that node for them.</p> <p>Complete user control of their own data. End-to-End-Encrypted too.</p> <h1 id="licenses"> Licenses <a class="heading-link" href="#licenses"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <h2 id="functional-source-license-fsl"> <a href="https://fsl.software/" class="external-link" target="_blank" rel="noopener">Functional Source License (FSL)</a> <a class="heading-link" href="#functional-source-license-fsl"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>The <a href="https://fsl.software/" class="external-link" target="_blank" rel="noopener">Functional Source License (FSL)</a> is a <a href="https://fair.io/" class="external-link" target="_blank" rel="noopener">Fair Source</a> license that converts to Apache 2.0 or MIT after two years. It is designed for SaaS companies that value both user freedom and developer sustainability. FSL provides everything a developer needs to use and learn from your software without <a href="https://en.wikipedia.org/wiki/Free-rider_problem" class="external-link" target="_blank" rel="noopener">harmful free-riding</a>.</p> <p>This is the closest thing to what I want. It allows the use of the code for non-competing purposes, and explicitly allows for non-commercial research, education, and even internal use. Basically, it&rsquo;s for SaaS companies to release their code under a license that allows all uses except those that undermine their business.</p> <p>But I don&rsquo;t have a product I&rsquo;m selling, and I don&rsquo;t want to be in the business of selling a SaaS product. Technically I could host a Synchronization Node and sell access, but I don&rsquo;t think the FSL would be appropriate for the library anyway. What is a &ldquo;Competing Use&rdquo; to a free library?</p> <p>One option to consider is to split the licensing for my library and my binary. License the binary under FSL and the library under something more open, but the reality is that all the goodies live inside the <em>library</em> and not the binary, so that they can be embedded.</p> <p>I love the idea here, and will fully advocate that anybody developing a SaaS should consider it, but it does not work for Eidetica.</p> <h2 id="agpl"> <a href="https://choosealicense.com/licenses/agpl-3.0/" class="external-link" target="_blank" rel="noopener">AGPL</a> <a class="heading-link" href="#agpl"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>AGPL is the strongest copyleft license that legally forces anyone using your library and providing access over a network to release all their source code. Technically it doesn&rsquo;t <em>say</em> that, but it has never been tested in court whether communicating with AGPL-licensed software <em>at all</em> makes your code required to be available. No matter what Free Software advocates may claim, multiple companies with huge legal teams ban it entirely because they are not sure how a court would interpret it.</p> <p>And <em>that</em> is why a bunch of companies with open source communities and a desire to make money use it. <a href="https://redis.io/blog/agplv3/" class="external-link" target="_blank" rel="noopener">Redis</a> and <a href="https://element.io/blog/element-to-adopt-agplv3/" class="external-link" target="_blank" rel="noopener">Matrix/Element</a> just off the top of my head.</p> <p>They license the code as AGPL plus commercial. So you can use it in OSS software as AGPL software while they also retain the right to sell exceptions. It&rsquo;s how they manage to make money at all, though in both of those cases they also sell hosted versions.</p> <p>This is the license and plan I’m currently using, but I am wary.</p> <p>The issue is that I want OSS projects to use it. And you simply can&rsquo;t license a <em>library</em> as AGPL because it functionally forces anything depending on it to also be AGPL. OSS projects can&rsquo;t just change their license easily* (all contributors must agree to the new license), so this makes the library a complete non-starter for most existing projects.</p> <p>*To clarify, an OSS project could in theory add an AGPL dependency without changing its license, however any resulting binary that contained the library would need to be AGPL. Yes it&rsquo;s a little confusing.</p> <p>Because of the uncertainty and hurdles, choosing AGPL will heavily limit adoption, and the value of this is quite literally in the network effects.</p> <h2 id="permissive-apachemitetc"> Permissive (Apache/MIT/etc) <a class="heading-link" href="#permissive-apachemitetc"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>A fully permissive license would place virtually no restrictions on the code except for attribution.</p> <p>Everybody could use it, everybody could make money off of it, and nobody has to pay for it in any way.</p> <p>Oh…</p> <h2 id="free-software-but-paid"> Free Software, But Paid <a class="heading-link" href="#free-software-but-paid"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>And here we get to the real problem: the downside to a permissive license is that there is simply no way to make money off the software.</p> <ul> <li>Begging for donations does not work. The number of people who actually earn anything close to a reasonable ROI is approximately zero. I give a good amount of money to the OSS software I use (&gt;$500/year) but most people do not or cannot.</li> <li>Offering consulting/support sounds reasonable, but I&rsquo;m not sure how widespread this is in reality. It also causes conflicts of interest that can reverberate through OSS communities for years.</li> <li>Grants from OSS software support funds. I&rsquo;ve seen similar projects get funding through these.</li> <li>Selling it as packaged software isn&rsquo;t an option, it&rsquo;s free.</li> <li>Selling a SaaS myself as &ldquo;the expert who knows how to manage it&rdquo; is not something I want to do even if I could get paid for it. It will eventually support arbitrary, E2EE data&hellip; I&rsquo;m not going to host that for strangers.</li> </ul> <p>Buuut, I also have a secret power. I don&rsquo;t really <em>need</em> income to work on this full time. I am retired (<a href="https://en.wikipedia.org/wiki/FIRE_movement" class="external-link" target="_blank" rel="noopener">FIRE&rsquo;d</a>), so I do what I want, and what I want now is for this piece of software to exist.</p> <p>I am a proud <a href="https://www.joelonsoftware.com/2001/04/21/dont-let-architecture-astronauts-scare-you/" class="external-link" target="_blank" rel="noopener">Architecture Astronaut</a>, heavily prone to getting <a href="https://xkcd.com/356/" class="external-link" target="_blank" rel="noopener">nerd-sniped</a>, and with lots of time on my hands.</p> <p>I like the tech space. I enjoy digging deeply into so many different areas and getting to play with a useful, foundational piece of software. I also see deficiencies in the existing solutions I&rsquo;ve examined, and I don&rsquo;t want to go re-architect somebody else&rsquo;s project to fix those problems.</p> <p>That said, I also do not actually want to &lsquo;work&rsquo; for free, and I don&rsquo;t want people to make money while relying on this company-sized project that I have built. I will lose interest and move on.</p> <p>That’s sort of why I’m leaning towards just using Apache 2.0 and then hoping I can beg for enough donations to cover my direct expenses at least. Maybe it leads to more, maybe it’s just fun to work on for a bit.</p> <p><strong>To be perfectly clear, my expenses just to be an active software developer are in the thousands of dollars per year (home office, fast computer, hosting, developer tools, coffee, etc.). And since I&rsquo;m effectively working full-time for myself instead of taking a job, the opportunity cost is in the hundreds of thousands per year.</strong></p> <h1 id="my-plan"> My Plan <a class="heading-link" href="#my-plan"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>I’ve been getting closer to a minimally viable release, when I will start talking about it more publicly. For now I am the only one having written code for it and fully retain the rights to change the license however I want.</p> <p>I am leaning towards just placing all this code under Apache 2.0 and calling it a day. More than likely it won&rsquo;t gain wide usage and I will get bored of quixotically developing a database for nobody and move on to something else. <a href="https://en.wikipedia.org/wiki/TempleOS" class="external-link" target="_blank" rel="noopener">TempleOS</a> is not something to aspire to.</p> <p>If it <em>does</em> become something that people use, I will however attempt to earn money through shaming people for donations, support contracts, consulting, etc., and be very open about conflicts of interest if they arise.</p> <h1 id="conclusion"> Conclusion <a class="heading-link" href="#conclusion"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>After looking at this, I gotta say the landscape is bleak for anybody who wants to try to monetize a project that needs to be some form of OSS to gain usage. The only reason I can even consider going the permissive route is that I do not need to worry about paying for food.</p> <p>I don&rsquo;t see any licensing route here that would allow broad adoption, monetization, and restricting Amazon from stealing it without paying any money, all while still being &ldquo;acceptable&rdquo; to the OSS community.</p> <p>I do not see how this product could exist without someone donating a large amount of time and effort on developing it. Pay will almost certainly be less than minimum wage for what is high-skill work, and my altruism is not infinite.</p> Stay Out Of My (Project) $HOME https://jackson.dev/post/stay-out-of-my-project-home/ Mon, 30 Jun 2025 00:00:00 +0000 https://jackson.dev/post/stay-out-of-my-project-home/ <p>Warning, I&rsquo;m about to get on my <a href="https://jackson.dev/post/base-10-was-a-mistake/" class="external-link" target="_blank" rel="noopener">mild OCD soapbox</a> again.</p> <p>Too many development tools expect to get the privilege of a config file in the root directory of my projects. Many of them don&rsquo;t even allow it to be a hidden file—they just require a fully unhidden &ldquo;tool.yml&rdquo; file sitting right there in the root of your project.</p> <p>Stop it. Please.</p> <h2 id="linux-home-directory"> Linux HOME Directory <a class="heading-link" href="#linux-home-directory"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Once upon a time this was a serious problem for organizing files in the Linux HOME directory too. Tools thought way too much of themselves and added files, directories, etc. to your HOME folder. A lot of them still do!</p> <p>Eventually some form of &ldquo;Standard&rdquo; was formed. <a href="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html" class="external-link" target="_blank" rel="noopener">XDG Directory Specifications</a> laid out a set of folders and their uses that could be mostly agreed upon.</p> <ul> <li>~/.config: configuration files, often in ~/.config/toolname/</li> <li>~/.local/share: application data files</li> <li>~/.local/bin: user-specific executables</li> <li>~/.cache: non-essential cached data that can be deleted</li> <li>~/.local/state: persistent state data that should survive between restarts</li> </ul> <p>I&rsquo;m sure people complain about this system, but it&rsquo;s good enough to impose some minimum amount of organization on the HOME folder. It does its job.</p> <p>Not all projects use it, but it&rsquo;s common enough that if a project becomes large enough and <em>doesn&rsquo;t</em> support XDG directories, there&rsquo;s usually some people who will show up and beg for XDG support. They might even help!</p> <h2 id="projects"> Projects <a class="heading-link" href="#projects"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Code project repos are faced with the same problem, though on a smaller scale.</p> <p>Build systems have their own project layouts they either impose or prefer, but other tools often end up polluting the main project directory.</p> <p>Take a look at my <a href="https://eidetica.dev" class="external-link" target="_blank" rel="noopener">Eidetica</a> project that I&rsquo;m developing. I&rsquo;m still in early development stages, I don&rsquo;t have any release flows set up, it only uses a single primary coding language (Rust), and yet the home repo still has all the following:</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>├── Cargo.lock </span></span><span style="display:flex;"><span>├── Cargo.toml </span></span><span style="display:flex;"><span>├── LICENSE.txt </span></span><span style="display:flex;"><span>├── README.md </span></span><span style="display:flex;"><span>├── Taskfile.yml </span></span><span style="display:flex;"><span>├── cliff.toml </span></span><span style="display:flex;"><span>├── deny.toml </span></span><span style="display:flex;"><span>├── flake.lock </span></span><span style="display:flex;"><span>├── flake.nix </span></span><span style="display:flex;"><span>├── .prettierignore </span></span><span style="display:flex;"><span>└── .github/ </span></span></code></pre></div><p>It&rsquo;s not too bad yet, but the cruft has already started to form. More established projects are significantly worse.</p> <p>And that&rsquo;s <em>without</em> having set up Docker, CI beyond basic GitHub Actions, or many other common development tools yet. The Nix flake actually helps here by eliminating some tool configs that would otherwise live in the project root, but you can still see how quickly things accumulate.</p> <p>Some of those files just aren&rsquo;t important enough to deserve top billing in my project repo.</p> <h2 id="solving-directories-for-projects"> Solving Directories for Projects <a class="heading-link" href="#solving-directories-for-projects"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>There are several different proposals for project organization, but they mostly take the same obvious route: apply the XDG recommendations (or a subset) to project repos and the tools designed to work with them.</p> <p><a href="https://dot-config.github.io/" class="external-link" target="_blank" rel="noopener">dot-config</a> is, as far as I&rsquo;m aware, the most popular effort in this space even though it&rsquo;s still only a ~50 star GitHub project. They focus on pushing for tools to support config files in a <code>.config/</code> directory. That would solve many of these issues, but doesn&rsquo;t solve the whole problem.</p> <h3 id="prj-spec"> PRJ Spec <a class="heading-link" href="#prj-spec"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>The <a href="https://github.com/numtide/prj-spec" class="external-link" target="_blank" rel="noopener">PRJ spec</a> is far more complete.</p> <p>The PRJ spec takes a different approach than dot-config. Instead of just focusing on configuration files, it defines a comprehensive set of conventions for project-centric tools:</p> <ul> <li><strong>Project Root Detection</strong>: Tools should use <code>$PRJ_ROOT</code> environment variable, or implement their own heuristics (like looking for <code>.config</code> folders)</li> <li><strong>Configuration Home</strong>: Tool configs go in <code>$PRJ_ROOT/.config/</code> instead of scattered across the project root</li> <li><strong>Cache Management</strong>: Build caches and intermediate files go in either <code>$PRJ_ROOT/.cache/</code> or shared in <code>$XDG_CACHE_HOME/prj/$PRJ_ID</code> if projects have unique IDs</li> </ul> <p>The philosophy is that project-centric tools (linters, formatters, build systems) should follow the same organizational principles that XDG brought to user-centric tools. Instead of every tool inventing its own way to find the project root and scatter config files everywhere, they should follow consistent conventions.</p> <h2 id="status"> Status <a class="heading-link" href="#status"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Within a rounding error, no dev tools support these efforts. I&rsquo;m still stuck installing config files into the root of my repos.</p> <p>Personally, I try to follow PRJ spec principles when I have control over how to layout files in my project directories. Local scripting, testing, caching build artifacts for development or benchmarking, etc.</p> <p>My <a href="https://cmprss.dev" class="external-link" target="_blank" rel="noopener"><code>cmprss</code> tool</a> for example has a <a href="https://github.com/arcuru/cmprss/blob/main/bin/test.sh" class="external-link" target="_blank" rel="noopener">test script</a> that downloads example files into a PRJ-compliant cache directory instead of cluttering the project root.</p> <p>I know it won&rsquo;t actually be &lsquo;fixed&rsquo; any time soon, but oh boy would it be nice.</p> Optimizing Naively https://jackson.dev/post/optimizing-naively/ Sun, 01 Jun 2025 00:00:00 +0000 https://jackson.dev/post/optimizing-naively/ <p>Back in the day I honed my optimization chops while working on <a href="https://projecteuler.net/" class="external-link" target="_blank" rel="noopener">Project Euler</a> problems.</p> <p>I was dumb and young, and most of the things I tried were pointless, but I did stumble on a technique that I have kept using in the nearly 2 decades since.</p> <h2 id="write-the-slow-version-first"> Write the slow version first <a class="heading-link" href="#write-the-slow-version-first"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Project Euler problems have the stated intention that all of the prompts should be solvable in less than a minute of computation time with the right algorithm. I don&rsquo;t know if that&rsquo;s true for all of them, especially the latest ones, but I took that as a challenge to try to optimize my solutions to fit within that window, or often to just optimize the solution as much as possible.</p> <p>Usually it was simplest to write a naive solution that would be <em>horrifically</em> slow, with computation time in hours, days, or years. But that slow solution was <em>obviously correct</em>. It was short enough to read and understand pretty easily so I could be confident that is was correct.</p> <p>This was helped and guided by Project Euler problems usually providing a &ldquo;checkpoint&rdquo; solution, an example output from a portion of the problem. For example, a question might be &ldquo;how many primes are below 1,000,000?&rdquo; and they&rsquo;d give the answer for primes below 1,000 so that you could check your algorithm on a smaller input.</p> <p>What I would often do is write an &ldquo;obviously correct&rdquo; solution that solved the example, then do two things:</p> <ol> <li>Start the slow solution running on the full answer. Sometimes this was fast enough.</li> <li>Write a new version that was actually fast.</li> </ol> <p>While writing the fast version I still had access to the slow version for unit testing and comparison, which was immensely useful for further work.</p> <h2 id="a-recent-example"> A recent example <a class="heading-link" href="#a-recent-example"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>A few months ago I got nerd-sniped by a slight variation of this LeetCode problem: <a href="https://leetcode.com/problems/find-median-from-data-stream/" class="external-link" target="_blank" rel="noopener">Find Median from Data Stream</a></p> <blockquote> <p>For a stream of numbers, at each new input print out the XX%-tile number from the already seen inputs.</p> </blockquote> <p>This problem has an <em>obviously correct</em> solution: you just take all the inputs seen, sort them at every step, and print out the XX percentile. It&rsquo;s extremely slow, O(N * N log N), but is clearly correct. I placed that obviously correct solution <a href="https://github.com/arcuru/lib/blob/09b66e50d75a4db4b48a615c55aa87f45451f254/percentiletracker/src/lib.rs#L434C1-L474C6" class="external-link" target="_blank" rel="noopener">into my unit tests</a>.</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#c6a0f6">fn</span> <span style="color:#8aadf4">calculate_percentile</span><span style="color:#91d7e3;font-weight:bold">&lt;</span>T: <span style="color:#91d7e3">Clone</span><span style="color:#91d7e3;font-weight:bold">&gt;</span>(values: <span style="color:#c6a0f6">&amp;</span>[T], percentile: <span style="color:#ed8796">usize</span>) -&gt; <span style="color:#eed49f">T</span> { </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> index <span style="color:#91d7e3;font-weight:bold">=</span> (values.len() <span style="color:#91d7e3;font-weight:bold">*</span> percentile) <span style="color:#91d7e3;font-weight:bold">/</span> <span style="color:#f5a97f">100</span>; </span></span><span style="display:flex;"><span> values[index].clone() </span></span><span style="display:flex;"><span>} </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#6e738d">/// Test helper that inserts a sequence of values into a PercentileTracker and </span></span></span><span style="display:flex;"><span><span style="color:#6e738d">/// verifies that the calculated percentile matches the expected value at each step. </span></span></span><span style="display:flex;"><span><span style="color:#6e738d">/// </span></span></span><span style="display:flex;"><span><span style="color:#6e738d">/// # Parameters </span></span></span><span style="display:flex;"><span><span style="color:#6e738d">/// * `values` - A slice of values to insert </span></span></span><span style="display:flex;"><span><span style="color:#6e738d">/// * `percentile` - The percentile to track (0-100) </span></span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fn</span> <span style="color:#8aadf4">insert_and_verify</span><span style="color:#91d7e3;font-weight:bold">&lt;</span>T<span style="color:#91d7e3;font-weight:bold">&gt;</span>(values: <span style="color:#c6a0f6">&amp;</span>[T], percentile: <span style="color:#ed8796">usize</span>) </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">where</span> </span></span><span style="display:flex;"><span> T: <span style="color:#91d7e3">Clone</span> <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#91d7e3">Ord</span> <span style="color:#91d7e3;font-weight:bold">+</span> Debug <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#91d7e3">PartialEq</span>, </span></span><span style="display:flex;"><span>{ </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> <span style="color:#c6a0f6">mut</span> tracker <span style="color:#91d7e3;font-weight:bold">=</span> PercentileTracker::new(percentile); </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> <span style="color:#c6a0f6">mut</span> test_values <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">Vec</span>::new(); </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">for</span> value <span style="color:#c6a0f6">in</span> values { </span></span><span style="display:flex;"><span> tracker.insert(value.clone()); </span></span><span style="display:flex;"><span> test_values.push(value.clone()); </span></span><span style="display:flex;"><span> test_values.sort_unstable(); </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> expected <span style="color:#91d7e3;font-weight:bold">=</span> calculate_percentile(<span style="color:#91d7e3;font-weight:bold">&amp;</span>test_values, percentile); </span></span><span style="display:flex;"><span> <span style="color:#8aadf4">assert_eq!</span>( </span></span><span style="display:flex;"><span> tracker.get_percentile(), </span></span><span style="display:flex;"><span> expected, </span></span><span style="display:flex;"><span> <span style="color:#a6da95">&#34;Failed at insertion {:?}&#34;</span>, </span></span><span style="display:flex;"><span> value </span></span><span style="display:flex;"><span> ); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><h2 id="performance-results"> Performance Results <a class="heading-link" href="#performance-results"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>The &ldquo;classic&rdquo; solution to this that interviewers look for is using Priority Queues, one for each &ldquo;side&rdquo;. These have operations on the order of O(log N), so inserting a full stream of inputs is O(N log N). They usually have poor memory locality characteristics though, so they end up being relatively slow for inputs that don&rsquo;t fit into CPU cache.</p> <p>I wrote a more general, O(N) solution for this problem using a bucketing strategy that I named <strong>PercentileTracker</strong>. You can view my code and more details in the repo <a href="https://github.com/arcuru/lib/tree/main/percentiletracker" class="external-link" target="_blank" rel="noopener">https://github.com/arcuru/lib/tree/main/percentiletracker</a></p> <p><strong>PercentileTracker</strong> is relatively fast, and is able to process ~40 million inputs per second even at input sizes that don&rsquo;t fit in CPU cache. Here is the estimated time taken to process 400 million 64-bit ints as input, while also outputting the 50th percentile entry at every step.</p> <p><img src="https://jackson.dev/2025/400M-50.webp" alt="Performance benchmark showing processing time for 400M inputs"></p> <p>(Bench run using <a href="https://github.com/bheisler/criterion.rs" class="external-link" target="_blank" rel="noopener">criterion</a> on a Ryzen 9 7900)</p> <p>With 64-bit input integers, this is an effective throughput of over 300 MB/s. For comparison, the naive approach of sorting the entire dataset at every step would take several hours for this same workload. I&rsquo;m sure I could tweak the optimized version to get more performance out of it but I&rsquo;m happy stopping here for now. The focus was on testing out the dynamic bucketing solution rather than micro-optimizations, and I certainly had fun doing that.</p> <h2 id="benefits"> Benefits <a class="heading-link" href="#benefits"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>With an obviously correct solution you can write test cases extremely easily. It is not a replacement for unit testing edge cases, but it lets you <em>find</em> edge cases in your algorithm.</p> <p>An <em>obviously correct</em> solution has several useful benefits while working on an optimized version:</p> <ol> <li><strong>Reference solution for testing edge cases.</strong> Run your unit tests against both versions and confirm that they both have the behavior you want. The clarity of the simple solution makes it easy to ensure you have the correct algorithm and edge case behavior.</li> <li><strong>Enables &ldquo;fuzz testing&rdquo; of a solution to find edge cases non-deterministically.</strong> Generate valid inputs randomly, feed them to both solutions, and look for differences.</li> <li><strong>It can serve as documentation.</strong> A naive solution can help a future reader of the code understand the problem in more depth.</li> </ol> <p>For example, my PercentileTracker code was unit tested against the obviously correct solution during development. It helped catch some off-by-one errors, and helped find some test cases that I hadn&rsquo;t considered when writing the code. It also gave me confidence that my code worked at different input percentages and sizes without having to manually code and define edge cases.</p> <p>It also lets me run more comprehensive tests against small values for pretty cheap:</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#[test]</span> </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fn</span> <span style="color:#8aadf4">test_large_dataset</span>() { </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Use a seed to make the test deterministic </span></span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> <span style="color:#c6a0f6">mut</span> rng <span style="color:#91d7e3;font-weight:bold">=</span> ChaCha8Rng::seed_from_u64(<span style="color:#f5a97f">42</span>); </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> values <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> <span style="color:#c6a0f6">mut</span> v <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">Vec</span>::new(); </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Insert enough values to trigger bucket splitting </span></span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">for</span> _ <span style="color:#c6a0f6">in</span> <span style="color:#f5a97f">0</span><span style="color:#91d7e3;font-weight:bold">..</span>(<span style="color:#eed49f">MAX_BUCKET_SIZE</span> <span style="color:#91d7e3;font-weight:bold">*</span> <span style="color:#f5a97f">4</span>) { </span></span><span style="display:flex;"><span> v.push(rng.random::<span style="color:#91d7e3;font-weight:bold">&lt;</span><span style="color:#ed8796">i64</span><span style="color:#91d7e3;font-weight:bold">&gt;</span>()); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> v </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Test at multiple percentiles </span></span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">for</span> percentile <span style="color:#c6a0f6">in</span> [<span style="color:#f5a97f">1</span>, <span style="color:#f5a97f">10</span>, <span style="color:#f5a97f">20</span>, <span style="color:#f5a97f">30</span>, <span style="color:#f5a97f">40</span>, <span style="color:#f5a97f">50</span>, <span style="color:#f5a97f">60</span>, <span style="color:#f5a97f">70</span>, <span style="color:#f5a97f">80</span>, <span style="color:#f5a97f">90</span>, <span style="color:#f5a97f">95</span>, <span style="color:#f5a97f">99</span>] { </span></span><span style="display:flex;"><span> insert_and_verify(<span style="color:#91d7e3;font-weight:bold">&amp;</span>values, percentile); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><p>Honestly, it just gives me peace of mind that the faster version works without issue. Trading a little extra compute time for safer development and more correctness is a choice I&rsquo;ll make every time.</p> <h2 id="conclusion"> Conclusion <a class="heading-link" href="#conclusion"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Writing and using the naive solution is a powerful tool for writing optimized algorithms. Having access to a slow but provably correct solution allows for verification of much more complex code without all the headache of formal proofs.</p> Don't Sleep on BitNet https://jackson.dev/post/dont-sleep-on-bitnet/ Tue, 13 May 2025 00:00:00 +0000 https://jackson.dev/post/dont-sleep-on-bitnet/ <p>EDIT: Apparently I&rsquo;m not alone in feeling this way, here&rsquo;s a post from the same day I posted this: <a href="https://huggingface.co/blog/codys12/rl-2025" class="external-link" target="_blank" rel="noopener">All LLMs Will Be Sparse BitNet Hybrids</a></p> <p>I took a break from working on <a href="https://eidetica.dev" class="external-link" target="_blank" rel="noopener">my decentralized DB</a> to dive into the latest LLM research. As usual, I focused on opportunities for optimizations and performance improvements.</p> <p>This post is mainly a summary of my private notes I made while &lsquo;researching&rsquo;.</p> <p>I&rsquo;ve only looked at inference (generating output, i.e. actually using the models) as that should dominate the total compute cost, and it&rsquo;s what needs to be efficient for local-first computing.</p> <p>I read through a lot of techniques, but by far the technique with the most potential seems to be BitNet.</p> <p>BitNet uses ternary weights with the values of [-1, 0, 1], which gives it extremely appealing performance characteristics.</p> <h2 id="i-an-overview-of-llm-architecture"> I. An Overview of LLM Architecture <a class="heading-link" href="#i-an-overview-of-llm-architecture"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Before diving into optimization techniques, let me briefly explain where all the computation goes. At a simplified level an LLM consists of:</p> <ul> <li><strong>Weights</strong>: Parameters learned during training (the actual &ldquo;knowledge&rdquo;)</li> <li><strong>Activations</strong>: Intermediate values computed during text generation</li> <li><strong>Layers</strong>: Stacked transformations, each applying specific operations</li> <li><strong>KV Cache</strong>: Storage for already-processed tokens (grows with context length)</li> </ul> <p>When generating text, the model processes input through these layers, with the primary computational workhorses being:</p> <ol> <li> <p><strong>Matrix Multiplications (MatMuls)</strong>: These dominate compute time. Modern GPUs are built specifically to parallelize these operations, but they&rsquo;re still expensive.</p> </li> <li> <p><strong>Memory Bandwidth</strong>: Moving gigabytes of weight data between memory and computation units is often slower than the computation itself.</p> </li> <li> <p><strong>KV Cache Growth</strong>: As context length increases, the memory needed to store intermediate states grows dramatically. This is why longer chats with LLMs consume far more RAM.</p> </li> </ol> <p>These bottlenecks become acute when trying to run models locally rather than in data centers. Cloud providers can throw specialized hardware and custom chips at the problem. Your laptop&hellip; cannot.</p> <h3 id="model-sizes"> Model sizes <a class="heading-link" href="#model-sizes"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Taking a step back, I should point out how LLM sizing will likely end up working in the long run. These are just my assumptions based on the hardware realities of today.</p> <p>For practical deployment you can think of models in a few different tiers, each with very distinct capabilities:</p> <ol> <li><strong>Always-on assistant models</strong>: <ul> <li>Sub-8B parameters, heavily quantized to fit in RAM or on accelerators. Useful for simple interactions and tasks, and can hand off to more capable models as needed.</li> </ul> </li> <li><strong>Local Specialist models</strong>: <ul> <li>12-30B parameters with special training. You&rsquo;d run the largest models you can locally for specialized tasks, capable of more autonomy. &ldquo;Label all my photos with descriptions&rdquo;, &ldquo;Research a trip to Cleveland that I would like&rdquo;.</li> </ul> </li> <li><strong>Cloud fallback</strong>: <ul> <li>Full-precision 70B+ models for the hardest tasks. Almost everyone will access these models remotely.</li> </ul> </li> </ol> <p>The optimal sizes will depend on hardware constraints, but even modest laptops can run <strong>Local Specialist Models</strong> at acceptable speeds with aggressive quantization. BitNet would give that extra push of potentially <em>doubling</em> the parameter sizes that could be run locally, and would significantly improve the inference speed at the same time.</p> <p>At small sizes (1B-30B), like the ones <strong>most people will interact with frequently</strong>, doubling the number of params is a significant increase in capabilities. An 8B model is much more capable than a 4B model.</p> <h2 id="ii-existing-efficiency-hacks"> II. Existing Efficiency Hacks <a class="heading-link" href="#ii-existing-efficiency-hacks"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Several approaches aim to address these challenges with big efficiency wins, these are <em>all</em> in use in various forms today:</p> <h3 id="quantization-techniques"> Quantization Techniques <a class="heading-link" href="#quantization-techniques"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Quantization reduces the precision of model weights and activations. While 8-bit and 4-bit quantization are now standard practice, research is pushing toward even more extreme compression:</p> <ul> <li><strong>Traditional quantization (8-bit, 4-bit)</strong>: 4-bit weights often have very little performance degradation and are quite common in local inference today.</li> <li><strong>Extreme quantization (2-bit and below)</strong>: The current focus of several research groups.</li> </ul> <p>The ParetoQ paper (<a href="https://arxiv.org/abs/2502.02631" class="external-link" target="_blank" rel="noopener">arXiv:2502.02631</a>) found something particularly interesting: 1.58-bit (3 values that vary per layer) or 2-bit (4 values, varying per layer) quantization may represent an optimal balance between compression and capability. For a fixed compute budget, you&rsquo;re often better off running a larger model at lower precision than a smaller model at higher precision.</p> <p>To get the most out of it though, you need to further finetune the quantized model to regain as much performance as possible after quantization. They show that it&rsquo;s possible to regain a lot of the capabilities &rsquo;lost&rsquo; when quantizing by finetuning on 10-30 Billion additional tokens after the quantization.</p> <p>This is a very promising approach, suggesting that post-quantization finetuning may be worthwhile for miniaturizing an existing model.</p> <p>Note that this is <em>not</em> the same thing as BitNet. The 1.58-bit quants talked about in ParetoQ are of variable values, not the [-1, 0, 1] used by BitNet.</p> <h3 id="mixture-of-experts-moe-architectures"> Mixture of Experts (MoE) Architectures <a class="heading-link" href="#mixture-of-experts-moe-architectures"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>MoE models take a fundamentally different approach to scaling. Instead of activating all parameters for every token, they contain specialized &ldquo;expert&rdquo; networks that are selectively activated:</p> <ul> <li>Only a small fraction of the model&rsquo;s total parameters process each token</li> <li>This allows models to grow in parameter count without proportional compute increases</li> <li>For example, Qwen 3 has a model that is 30B total parameters with only 3B active to compute each token.</li> </ul> <p>You get some of the benefit of the full parameter count while only doing computation on a small fraction of it per token. The downside is that you need more memory to keep all the weights loaded.</p> <p>Faster inference, but higher memory than an equivalently capable &lsquo;dense&rsquo; model.</p> <p>Many of the largest open weight models do this because they are more efficient to train compared to a dense model of similar capability, and it&rsquo;s rumored that the proprietary models are probably MoE.</p> <h3 id="speculative-decoding"> Speculative Decoding <a class="heading-link" href="#speculative-decoding"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>This approach uses a smaller, faster model to pre-generate likely outputs, which the larger model then verifies rather than generating from scratch. Google&rsquo;s Speculative Decoding paper (<a href="https://arxiv.org/abs/2211.17192" class="external-link" target="_blank" rel="noopener">arXiv:2211.17192</a>) demonstrated speedups of 2-5x using this technique.</p> <p>It can also be built into the model itself, as in <a href="https://github.com/XiaomiMiMo/MiMo" class="external-link" target="_blank" rel="noopener">Xiaomi&rsquo;s MiMo</a> and I&rsquo;m sure many others.</p> <h2 id="iii-bitnet-b158"> III. BitNet b1.58 <a class="heading-link" href="#iii-bitnet-b158"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>In my opinion, the most promising approach that should be pursued further is BitNet. The model sizes are similar to extreme quantization, but it allows for a fundamental change to the underlying math that meshes extremely well with the hardware realities.</p> <h3 id="what-is-bitnet-b158"> What is BitNet b1.58? <a class="heading-link" href="#what-is-bitnet-b158"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>BitNet represents weights using only three possible values: -1, 0, and 1. This is called ternary quantization, using approximately 1.58 bits per parameter (log₂(3) ≈ 1.58). Unlike traditional quantization that starts with a full-precision model and then compresses it, BitNet models are trained from scratch with these constraints.</p> <p>(Important note: this 1.58-bit is <strong>not the same as in ParetoQ</strong>. ParetoQ used values other than [-1, 0, 1] that change per layer.)</p> <h3 id="why-its-cool"> Why It&rsquo;s Cool <a class="heading-link" href="#why-its-cool"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>BitNet&rsquo;s approach offers several advantages:</p> <ol> <li> <p><strong>MatMul Becomes Addition</strong>: When weights are limited to -1, 0, and 1, multiplication operations are replaced with much simpler operations:</p> <ul> <li>Multiplying by 0: Result is 0 (skip the operation)</li> <li>Multiplying by 1: Result is unchanged (just copy the value)</li> <li>Multiplying by -1: Result is negated (single-cycle operation)</li> </ul> <p>On modern CPUs, multiplication operations typically take multiple cycles, while additions and negations take just one. <strong>They also require significantly more die space and power</strong>.</p> </li> <li> <p><strong>Tiny Memory Footprint</strong>: Representing weights with just 1.58 bits matches even the smallest quants, allowing much larger models to fit in limited memory.</p> </li> <li> <p><strong>Hardware Acceleration Potential</strong>: The simplicity of BitNet&rsquo;s operations makes it ideal for specialized hardware acceleration.</p> </li> </ol> <h3 id="bitnet-v2"> BitNet v2 <a class="heading-link" href="#bitnet-v2"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>The BitNet researchers recently published a follow-up paper (<a href="https://arxiv.org/abs/2504.18415" class="external-link" target="_blank" rel="noopener">arXiv:2504.18415</a>) introducing BitNet v2, which combines 1.58-bit weights with 4-bit activations and a technique called Hadamard transformation. This approach takes advantage of recent hardware support for 4-bit math while maintaining the core benefits of the BitNet approach.</p> <p>It uses 4-bit math for Activations inside the model instead of 8-bit math, but the inputs are still the same size (8 bits).</p> <p>In short, by switching to using 4-bit Activations with the same size model they can:</p> <ol> <li>Keep the same size on disk</li> <li>~Halve the size of the KV Cache, decreasing how much memory is used by the context</li> <li>Use INT4 operations, potentially <em>doubling</em> the theoretical computational throughput</li> </ol> <p>Initial results show these extremely compressed models can achieve performance comparable to BitNet b1.58, which itself is similar to the performance of models with larger quant sizes and the same parameter count.</p> <p>I&rsquo;ll repeat that last point, <strong>they are claiming comparable performance to models that are twice the size in RAM, and they&rsquo;re doing it while also getting rid of the multiplies</strong>.</p> <h2 id="iv-so-what"> IV. So What? <a class="heading-link" href="#iv-so-what"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>A BitNet model with an optimized library to run it should be extremely efficient even for CPU inference, here is a chart from <a href="https://github.com/microsoft/BitNet" class="external-link" target="_blank" rel="noopener">the BitNet repo</a> showing inference tokens/second on &lsquo;dummy&rsquo; models of different sizes.</p> <p><img src="https://jackson.dev/2025/intel_performance.jpg" alt="Intel 13700K Tokens / Second"></p> <p>Keep in mind that they are also claiming that they also achieve similar performance to existing models of the same size. Those numbers are significantly higher than what you&rsquo;d get for similar sized models using CPU inference.</p> <p>Those claimed numbers are roughly comparable to running existing models on my not very small GPU.</p> <h3 id="synergy-with-moe-architectures"> Synergy with MoE Architectures <a class="heading-link" href="#synergy-with-moe-architectures"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>BitNet and MoE architectures complement each other beautifully. If BitNet allows us to significantly reduce the memory footprint of weights, we could:</p> <ol> <li>Fit much larger models in the same memory</li> <li>Increase the number of experts in an MoE model without increasing memory requirements</li> <li>Deploy sophisticated models on devices that previously couldn&rsquo;t support them</li> </ol> <p>This synergy could be particularly powerful for local computing, where memory is often the primary constraint.</p> <p>Qwen 3&rsquo;s largest model is a 235B parameter model with 22B active parameters. A BitNet v2 model of that size would only be ~80 GB and would be runnable at ~5-10 tokens / second on a high-end Desktop, <strong>without offloading to the GPU</strong>.</p> <h3 id="the-efficiency-frontier"> The Efficiency Frontier <a class="heading-link" href="#the-efficiency-frontier"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>When evaluating model performance, we need to consider not just raw accuracy but efficiency. The relevant question isn&rsquo;t &ldquo;How does a 2-bit model compare to a full-precision model?&rdquo; but rather &ldquo;How does a larger 2-bit model compare to a smaller full-precision model when both use the same amount of computation or memory?&rdquo;</p> <p>Most of the literature compares against models of a similar parameter size because that is <em>normally</em> a good proxy for performance and memory requirements, but that falls apart for BitNet.</p> <p>There&rsquo;s somewhat limited data comparing like-for-like models computationally, which I find really odd. If BitNet can scale, it seems like it would be significantly more efficient when comparing to models of similar performance.</p> <h3 id="its-not-ready-yet"> It&rsquo;s Not Ready (yet) <a class="heading-link" href="#its-not-ready-yet"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>While BitNet can run on existing hardware, its full potential will come through specialized chips:</p> <ol> <li><strong>Software implementations</strong> on current hardware (modest gains)</li> <li><strong>Optimized implementations</strong> leveraging existing hardware features (significant gains)</li> <li><strong>Custom accelerators</strong> designed specifically for BitNet-style operations (transformative gains)</li> </ol> <p>The simplicity of BitNet operations makes it an ideal candidate for custom silicon that could achieve orders of magnitude better efficiency than general-purpose processors, though writing optimized CUDA kernels to take advantage of the INT4 math on the latest NVIDIA GPUs would still be impressive perf wise.</p> <h2 id="v-open-research-questions"> V. Open Research Questions <a class="heading-link" href="#v-open-research-questions"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Several important challenges remain:</p> <ol> <li> <p><strong>Training efficiency</strong>: Training BitNet models from scratch is computationally expensive, but it seems like the ternary models may require it. I couldn&rsquo;t find research on trying to quantize a larger model to [-1, 0, 1] weights, but maybe someone will find a usable technique. Regardless, even if it&rsquo;s more expensive to train, it&rsquo;s significantly more efficient in inference so the total compute over its lifetime should be smaller.</p> </li> <li> <p><strong>Capability retention</strong>: More research is needed to understand which capabilities are preserved and which degrade as you scale up the training and parameter size.</p> </li> <li> <p><strong>Hardware/software co-design</strong>: The full potential of BitNet will only be realized through tight integration between hardware and software design.</p> </li> </ol> <h2 id="conclusion"> Conclusion <a class="heading-link" href="#conclusion"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>BitNet&rsquo;s 1.58-bit approach represents one of the most promising paths toward dramatically more efficient LLMs. I&rsquo;m shocked there doesn&rsquo;t seem to be more work going into it given its potential.</p> <p>I don&rsquo;t have the money, but I&rsquo;m intrigued by the idea of training a decent sized model (14B-32B range) to better test how it performs. The BitNet team has been doing research on small models up to 2B parameters, so it remains to be seen if the capabilities scale as well as higher quant models, and proving capabilities in a 32B model would be a really strong proof of concept.</p> <h2 id="references"> References <a class="heading-link" href="#references"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <ul> <li>Microsoft BitNet <ul> <li>BitNet v1: <a href="https://arxiv.org/abs/2402.17764" class="external-link" target="_blank" rel="noopener">arXiv:2402.17764</a></li> <li>BitNet v2: <a href="https://arxiv.org/abs/2504.18415" class="external-link" target="_blank" rel="noopener">arXiv:2504.18415</a></li> </ul> </li> <li>ParetoQ: <a href="https://arxiv.org/abs/2502.02631" class="external-link" target="_blank" rel="noopener">arXiv:2502.02631</a></li> </ul> You're Holding AI Wrong https://jackson.dev/post/youre-holding-ai-wrong/ Fri, 04 Apr 2025 00:00:00 +0000 https://jackson.dev/post/youre-holding-ai-wrong/ <p>If you&rsquo;re old enough you may remember &ldquo;Antennagate&rdquo; from 2010, when iPhone users were told they should avoid gripping the phone in certain ways. The phrase &ldquo;you&rsquo;re holding it wrong&rdquo; became shorthand for blaming users instead of fixing bad design.</p> <p>Apple, of course, was trying to blame users instead of admitting their own problems.</p> <p>But here&rsquo;s the thing: sometimes it really is the users who are holding it wrong. And that&rsquo;s exactly what I&rsquo;m seeing with the current crop of generative AI tools.</p> <h2 id="the-problem-with-how-we-use-ai"> The Problem with How We Use AI <a class="heading-link" href="#the-problem-with-how-we-use-ai"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I see a lot of people using generative AI wrong. Some treat it like Google, expecting perfect factual answers. Others approach it like an all-knowing oracle that will solve their problems with minimal input. And then there are those who seem to only want to find its faults, poking and prodding until they get hallucinations they can screenshot and mock online.</p> <p>It&rsquo;s none of those things. Yes, it may be <em>just</em> very fancy autocomplete, but it&rsquo;s more than that too. It takes a while of using these tools to get a good feel for them but it&rsquo;s becoming increasingly clear that this tech will fundamentally change how we interact with the world. It&rsquo;s best to be prepared.</p> <p>I&rsquo;ve used these tools extensively for coding, and I wanted to explain to my non-coding friends how to use them properly.</p> <p>Fundamentally what most people don&rsquo;t understand is that this crop of AI <strong>should be treated as <em>tools</em>, not as full <em>solutions</em>.</strong></p> <h2 id="writing-a-blog-post-the-ai-augmented-way"> Writing a Blog Post: The AI-Augmented Way <a class="heading-link" href="#writing-a-blog-post-the-ai-augmented-way"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>To try to be as broad as possible, let&rsquo;s walk through the process for writing a blog post with AI assistance:</p> <ol> <li><strong>You come up with an idea for a post.</strong> This is critical. The AI doesn&rsquo;t decide what you want to write about—you do. The initial creative spark still comes from you</li> <li><strong>Write personal notes on your direction.</strong> Jot down your key points, the tone you want to strike, and any specific angles you want to cover. These notes will guide both you and the AI throughout the process.</li> <li><strong>If needed, gather supporting information.</strong> Use search tools like Google or Perplexity to gather research and references. I&rsquo;d recommend sending well-formed questions to the various &ldquo;Deep Research&rdquo; tools. Perplexity, OpenAI, and Google all have one. They iteratively search and research a topic for you, potentially saving time while researching and collating the data.</li> <li><strong>Summarize your research.</strong> Take that data and strip it down to only the important bits needed to have a full understanding of the context for the post. This step helps you organize your thoughts, come to your own conclusions, and decide what makes sense to include, drop, or save for later.</li> <li><strong>Generate multiple drafts.</strong> Send the resulting collection of notes, outlines, and research to multiple LLMs and instruct them to write a draft. Different models &ldquo;write&rdquo; differently, so your favorite may vary. If you want to get fancy, include example blog posts you&rsquo;ve already written and say &ldquo;do it like this.&rdquo;</li> <li><strong>Provide feedback and iterate.</strong> Give clear feedback to the LLMs if you want them to adjust. This is where you shape the output to match your vision, not just accept whatever it spits out.</li> <li><strong>Synthesize the final product yourself.</strong> Gather the best 2-3 drafts, and from those, write the actual blog post. Take what you like, toss what you don&rsquo;t. Maybe go back and have the models rewrite specific sections that aren&rsquo;t quite right.</li> <li><strong>Repeat steps 5-7 as desired</strong>. Generate drafts, edit, refine, until you&rsquo;re done.</li> </ol> <p>In this new paradigm, words are abundant. What&rsquo;s scarce is your perspective, creativity, and expertise. Generative AI can&rsquo;t replace that. What it can do is amplify your abilities, save you time, and help you explore ideas in ways you might not have considered on your own.</p> <p>So, if you&rsquo;re treating AI like a search engine or expecting it to do all the work, you&rsquo;re holding it wrong. Think of it as a collaborator—a tool that requires practice, patience, and a clear sense of direction.</p> <p>The best way to get better? Just try it.</p> <h2 id="extra-credit"> Extra Credit <a class="heading-link" href="#extra-credit"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>If you&rsquo;re a bit more technical, there&rsquo;s a powerful trick you can borrow from software development: use a version control system like <code>git</code> to track your drafts.</p> <p>Think of it as a time machine for your writing. Every time you make a change, you can save a snapshot. Later, you can see exactly what you added, removed, or rephrased—line by line. This makes it much easier to experiment, knowing you can always roll back or compare different versions.</p> <p>You can even branch off multiple directions—try a more casual tone on one branch, a more formal style on another—and then merge the best parts together. It&rsquo;s a great way to iterate quickly without losing anything along the way.</p> <p>It might sound complicated, but even basic use of git can supercharge your editing process. And if you&rsquo;re collaborating with others it&rsquo;s invaluable for managing feedback and revisions.</p> <p>Treat your drafts like code: version early, version often.</p> <h2 id="the-human-element-is-essential"> The Human Element Is Essential <a class="heading-link" href="#the-human-element-is-essential"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>What you bring, as the human, is expertise and judgment. You guide things toward what you want the output to be. You toss things you don&rsquo;t like. You give these tools feedback and steer them toward your vision rather than accepting whatever they produce.</p> <p>The AI doesn&rsquo;t replace you—it amplifies you. It gives you a massive boost in productivity and capabilities, but only if you&rsquo;re holding it right.</p> <p>Personally I get a lot of value out of using them to remove things that are often roadblocks for me. I can take notes and generate a blog post directly <em>from the raw notes</em>. I don&rsquo;t need to spend an hour fixing up grammar or thinking through different clever phrases, I can just use these new tools to fix that. I can focus my attention on the bigger things.</p> <p>These tools can take work to understand. My main suggestion is that you try them occasionally to see if you can fit them into your workflow. Go to <a href="http://openrouter.ai/" class="external-link" target="_blank" rel="noopener">openrouter.ai</a> to access any model, not just one from a single company. Sometimes you&rsquo;ll find that you prefer the &ldquo;voice&rdquo; of different models for different types of tasks.</p> <p>Some will be better at technical writing, others at creative work, and still others at summarization or research. Finding the right tool for each job is part of the process.</p> <h2 id="conclusion"> Conclusion <a class="heading-link" href="#conclusion"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>We&rsquo;re in the early days of this technology, and most people are still figuring out how to use it effectively. Take the time to learn how these tools actually work and how to get the most out of them.</p> <p>And yes, of course this post is a description of how I wrote this post. I also used it to help write some of my previous post <a href="https://jackson.dev/post/base-10-was-a-mistake/" >Base 10 Was a Mistake</a>. I&rsquo;m not sure I like how they came out though, it&rsquo;s a little difficult to get the tone and flow to sound natural. I ended up cutting/editing quite a bit and the end result is still a little bit wordy overall.</p> Base 10 was a mistake https://jackson.dev/post/base-10-was-a-mistake/ Thu, 27 Mar 2025 00:00:00 +0000 https://jackson.dev/post/base-10-was-a-mistake/ <p>I’m stepping onto my mild OCD soapbox today. But I needed to get this out there.</p> <p>Base 10 was a mistake. I want to curse the evolutionary gods for giving us 10 fingers instead of 12.</p> <h2 id="the-problem-with-base-10"> The Problem with Base 10 <a class="heading-link" href="#the-problem-with-base-10"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Our decimal number system is built on an accident of human anatomy. We have ten fingers, so we count in base 10. That’s it—that&rsquo;s the entire reason. We&rsquo;ve built our entire mathematical system on &ldquo;well, I ran out of fingers to count on.&rdquo;</p> <p>And you know what? That&rsquo;s actually kind of terrible. Base 10 gives us exactly three factors to work with: 1, 2, and 5. That&rsquo;s why we spend so much time dealing with repeating decimals and awkward fractions. Want to divide something into thirds? Hope you like writing 0.33333&hellip; forever. Want to split something into quarters? 0.25 isn&rsquo;t terrible, but it&rsquo;s not great either.</p> <h2 id="why-base-12-is-better"> Why Base 12 Is Better <a class="heading-link" href="#why-base-12-is-better"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Base 12, on the other hand, is <em>much</em> better. It has twice as many factors: 1, 2, 3, 4, 6, and 12. This means more numbers divide evenly, which makes mental math easier and decimals cleaner.</p> <p>Let&rsquo;s look at some common fractions:</p> <ul> <li>1/3 in base 10: 0.333333&hellip;</li> <li>1/3 in base 12: 0.4 (clean and done)</li> <li>1/4 in base 10: 0.25</li> <li>1/4 in base 12: 0.3 (even cleaner)</li> </ul> <p>We actually still use base 12 thinking in a lot of places:</p> <ul> <li>12 months in a year</li> <li>12 hours on a clock face</li> <li>12 inches in a foot</li> <li>12 units in a dozen</li> </ul> <p>You know why? Because it&rsquo;s genuinely more practical for everyday use.</p> <h2 id="the-real-world-impact"> The Real-World Impact <a class="heading-link" href="#the-real-world-impact"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I can already hear you saying &ldquo;who cares about clean fractions?&rdquo; Well, anyone who&rsquo;s ever:</p> <ul> <li>Split a restaurant bill three ways</li> <li>Tried to divide their time evenly between projects</li> <li>Needed to calculate percentages in their head</li> <li>Worked with measurements in construction</li> <li>Done any kind of financial calculations</li> </ul> <p>So&hellip; basically everyone.</p> <h2 id="why-were-stuck-with-base-10"> Why We&rsquo;re Stuck With Base 10 <a class="heading-link" href="#why-were-stuck-with-base-10"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>The problem is, switching to base 12 would be an absolute nightmare. We&rsquo;d need to:</p> <ol> <li>Retrain literally everyone in mathematics</li> <li>Replace every numeric display ever made</li> <li>Update all our software (I can already hear the COBOL maintainers screaming)</li> <li>Convince people that &ldquo;10&rdquo; means twelve now</li> <li>Probably rename all the numbers to avoid confusion</li> </ol> <p>And that&rsquo;s just the start.</p> <p>The &ldquo;right&rdquo; solution would be to slowly transition to base 12 over several generations. Start teaching it in schools alongside base 10, begin using it in scientific contexts, and gradually phase it in as the old system phases out.</p> <p>But that’s never going to happen. We&rsquo;re stuck with our ten-fingered counting system forever, watching our calculators spit out endless repeating decimals because our ancient ancestors couldn&rsquo;t think past their hands.</p> <h2 id="conclusion"> Conclusion <a class="heading-link" href="#conclusion"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Base 12 is better than base 10 in almost every way that matters for real world use. It&rsquo;s more practical, more divisible, and would make everyone&rsquo;s lives easier if we used it. But we won’t, because sometimes the cost of fixing a suboptimal system is higher than the cost of living with it.</p> <p>Kind of like that legacy codebase you&rsquo;re maintaining that really needs to be rewritten but &ldquo;works fine&rdquo; so it&rsquo;ll never get approved. You know the one.</p> <p>Maybe I’ll go join the <a href="https://dozenal.org/" class="external-link" target="_blank" rel="noopener">Dozenal Society of America</a> to help spread the good word.</p> Generative AI and Copyright https://jackson.dev/post/generative-ai-and-copyright/ Sat, 22 Feb 2025 00:00:00 +0000 https://jackson.dev/post/generative-ai-and-copyright/ <p>Disclaimer: I am a Software Engineer. I am not an Economist, nor am I a Lawyer, and even I know that this isn&rsquo;t as simple as it sounds.</p> <p><strong>TL;DR - I&rsquo;m proposing that we allow copyrighted material to be used for training AI models, and in return we should institute a new tax on the profits from those AIs.</strong></p> <p>The current crop of AI models are <em>not possible</em> without utilizing large amounts of copyrighted material in their training.</p> <p>Meta has already been caught torrenting 80TB of books to use for training. It&rsquo;s safe to assume that their Llama models have been trained on every book and scientific paper ever written.</p> <p>And if you think the other companies did not do the same thing, you are naive. Model performance heavily relies on the size and quality of the training data, it is generally accepted that all the leading models were trained on copyrighted material.</p> <p>Legally acquiring the rights to <em>all</em> of that data would be prohibitively expensive. Maybe Microsoft or Meta or Google could actually afford to pay for all the copyrighted material ever created, but startups certainly couldn&rsquo;t.</p> <p>What we as a society should want out of this technology is for it to be democratized. We should celebrate startups and individuals building their own AI models, we want as many people as possible to be able to compete and innovate in this new landscape.</p> <p>However we also need to recognize that those innovations are only possible because of the immense amount of human ingenuity and effort that has gone into creating copyrighted works. It is high quality data that the AI&rsquo;s can learn from, and <em>it is valuable.</em> But it&rsquo;s almost impossible to point to a specific copyrighted work as more valuable than the rest.</p> <p>So I have a proposal, let&rsquo;s put all the AI companies on a payment plan.</p> <ol> <li>You should be allowed to train an AI on all copyrighted material, with caveats.</li> <li>We should tax the fuck out of any profits from the resulting AI and use it for the public good.</li> </ol> <p>In effect, I&rsquo;m proposing that we declare aggregated copyrighted works a public good. They are the product of humanity, and should be used for the benefit of all people.</p> <p>This would provide a funding source for those displaced by AI jobs, and ensure that companies located in countries with strict copyright laws can <strong>legally</strong> compete with their competitors that operate in countries without such restrictions.</p> <p>Japan already has laws on the books that allow the use of copyright materials for training AI models, let&rsquo;s extend that to also embrace taxing them for the public good.</p> Why I Switched from Cody to Continue.dev https://jackson.dev/post/cody-hates-reset/ Wed, 18 Dec 2024 00:00:00 +0000 https://jackson.dev/post/cody-hates-reset/ <p>An AI assistant has become an invaluable part of my development workflow over the past year, especially over the last several months. I started with Copilot, switched to Cody for a few months, and am now using Continue.dev.</p> <p>I was really happy using Cody (made by <a href="https://sourcegraph.com/" class="external-link" target="_blank" rel="noopener">Sourcegraph</a>), and was happily recommending it to friends, so I wanted to lay out why I stopped using them and switched to Continue.dev.</p> <h2 id="background-on-ai-assistants"> Background on AI Assistants <a class="heading-link" href="#background-on-ai-assistants"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>If you aren&rsquo;t familiar with AI assistants, they are frontends to usually the same underlying LLMs.</p> <p>They may use different models for various components, run some things locally or remotely, etc., but they more or less have access to the same backend models.</p> <p>AI assistants bring two distinct pieces of value:</p> <ol> <li>User experience, by integrating into your coding workflow.</li> <li>Smart usage of context to feed to the underlying models.</li> </ol> <p>The second part is very important in how useful any queries to the LLMs can be. It&rsquo;s all about getting the right <em>context</em> for any given query.</p> <p>When you ask a question or want autocompletion, the model needs to be fed some subset of your codebase or docs to give the best response. Knowing exactly what context to provide is a pretty difficult problem, as you need a good understanding of the codebase to know which parts are actually relevant to any query.</p> <p>Sourcegraph&rsquo;s entire business is in understanding and searching codebases. They <em>should</em> have a huge advantage in gathering the right context for queries.</p> <p>I can work around UX issues, but I am willing to pay real money for a perceivable improvement in LLM answer quality, and Sourcegraph is best positioned to do that.</p> <p>And until I hit this particularly problematic bug, I was an extremely happy customer.</p> <h2 id="the-problem"> The Problem <a class="heading-link" href="#the-problem"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <h3 id="my-bug-report"> My Bug Report <a class="heading-link" href="#my-bug-report"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Cody would consistently refuse to answer questions in two of my repositories when allowing it to choose context from the codebases. After hitting this issue multiple times, I reached out to their support team to understand what was happening.</p> <p>The errors were essentially, &ldquo;Your request was blocked, here&rsquo;s an ID to use with customer service.&rdquo;</p> <p>All customer service could tell me about my specific request was that it contained the word &ldquo;Reset&rdquo;, which caused the request to be blocked.</p> <p>Here was my response, which I think sums up the situation well, so I am including it as is:</p> <blockquote> <p>Thank you for the information. With that I was able to find the problem, though I would suggest that this is forwarded to your development team for triage.</p> </blockquote> <blockquote> <p>Cody is including context from the Changelog file. In both repos, the changelog file contains the line &ldquo;- Add a clear command to reset the session&rdquo;.</p> </blockquote> <blockquote> <p>Removing that line allows the request to go through.</p> </blockquote> <blockquote> <p>I would suggest that Cody is updated to handle that situation more elegantly, as Cody is the one that decided to include that line as context. I was not even directly asking a question about the Changelog file.</p> </blockquote> <blockquote> <p>The infringing lines that break requests for these repos:</p> </blockquote> <blockquote> <p><a href="https://github.com/arcuru/chaz/blob/58c20aa736f9a71ae6e8237809643fc63de92f40/CHANGELOG.md?plain=1#L20" class="external-link" target="_blank" rel="noopener">https://github.com/arcuru/chaz/blob/58c20aa736f9a71ae6e8237809643fc63de92f40/CHANGELOG.md?plain=1#L20</a></p> </blockquote> <blockquote> <p><a href="https://github.com/arcuru/headjack/blob/f04922d79c7586cc5e99870e7d45d8ee387b0768/CHANGELOG.md?plain=1#L48" class="external-link" target="_blank" rel="noopener">https://github.com/arcuru/headjack/blob/f04922d79c7586cc5e99870e7d45d8ee387b0768/CHANGELOG.md?plain=1#L48</a></p> </blockquote> <blockquote> <p>I&rsquo;m&hellip;not going to edit my auto-generated Changelog so that I can use Cody in those repos, and there seems to be no way for me to otherwise fix this on my side without maintaining a local diff to that file.</p> </blockquote> <blockquote> <p>Thanks for the help :)</p> </blockquote> <p>It doesn&rsquo;t actually matter, but I do have some guesses as to why the word &ldquo;Reset&rdquo; was actually blocked that I included down below.</p> <h4 id="no-workarounds-exist-unless-you-pay-for-enterprise"> No Workarounds Exist&hellip; Unless You Pay for Enterprise <a class="heading-link" href="#no-workarounds-exist-unless-you-pay-for-enterprise"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h4> <p>The only way I could fix this would be to include context filtering into Cody, to tell it, &ldquo;Hey, never include the changelog file.&rdquo; This is not ideal. What if I used the phrase &ldquo;reset the session&rdquo; in a code comment too?</p> <p>But even then, Cody doesn&rsquo;t make that very easy.</p> <p>Cody apparently <em>did</em> have a way to filter individual files like that, but IIRC it was always behind an &rsquo;experimental flag&rsquo; and is now <a href="https://sourcegraph.com/docs/cody/capabilities/ignore-context#cody-ignore-files" class="external-link" target="_blank" rel="noopener">restricted to their Enterprise plan</a>.</p> <p>Which, like, come on, that&rsquo;s a crazy thing to put behind a paywall.</p> <p>Continue just has a <code>.continueignore</code> file that works exactly like <code>.gitignore</code>.</p> <h3 id="its-just-a-bug-why-leave"> It&rsquo;s Just a Bug. Why Leave? <a class="heading-link" href="#its-just-a-bug-why-leave"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Yes, and it&rsquo;s fixable, but:</p> <ol> <li>I was only using Cody for finding the right context. They had one job.</li> <li>Apparently, they try to block the word &ldquo;reset&rdquo; (or more likely &ldquo;reset the session&rdquo;, see below). I use that word occasionally, and banning the word &ldquo;reset&rdquo; is insane.</li> <li>Their context filtering is apparently so bad that it doesn&rsquo;t filter out context that they themselves block.</li> </ol> <p>I literally paid them to do a single job for me, and they were so bad at it that they didn&rsquo;t take into account their own requirements.</p> <h2 id="the-solution"> The Solution <a class="heading-link" href="#the-solution"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I switched to <a href="https://continue.dev" class="external-link" target="_blank" rel="noopener">Continue.dev</a>. In the spirit of all good OSS projects, they have worse UX but are way more configurable. I want to pay them to improve their UX, but it&rsquo;s free (bring your own keys) and I haven&rsquo;t figured out where I can give them money.</p> <p>I use a mix of a local LLM for autocompletion and bring my own API keys to use my preferred chat/embed/re-ranking remote models. For some weird reason (ahem&hellip; money), you have to buy the Enterprise plan from Cody to bring your own API keys (which actually would save Sourcegraph money, as you&rsquo;d be paying Sourcegraph just to be a plugin and would also be paying a different provider for the actual models).</p> <p>I also highly recommend that everyone using an assistant should go through Continue.dev&rsquo;s documentation and configure it. It was fantastic for helping me understand what each of the AI assistant components actually do. Even if you use something else, it can be really helpful. Links to the <a href="https://docs.continue.dev/" class="external-link" target="_blank" rel="noopener">User Guide</a> and the instructions to <a href="https://docs.continue.dev/customize/overview" class="external-link" target="_blank" rel="noopener">Customize</a> the plugin.</p> <h2 id="appendix"> Appendix <a class="heading-link" href="#appendix"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <h3 id="why-does-cody-hate-reset"> Why Does Cody Hate &ldquo;Reset&rdquo;? <a class="heading-link" href="#why-does-cody-hate-reset"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>So, this is their full explanation:</p> <blockquote> <p>Checking the reference ID you provided, the request seems to have been blocked because it included the word or phrase &ldquo;Reset,&rdquo; which our system flagged. For your privacy, I am unfortunately not able to see the full prompt, so I can&rsquo;t verify exactly what you sent, but I suggest you check the contents of your prompt.</p> </blockquote> <blockquote> <p>This is just a precaution to avoid any potentially harmful actions. The prompt_prefix I shared above gives us a glimpse of the user&rsquo;s input and the code referenced, which may have triggered the block.</p> </blockquote> <p>I&rsquo;m guessing they actually block &ldquo;reset the session&rdquo; or a similar intent, not just the word &ldquo;Reset.&rdquo;</p> <p>What they&rsquo;re probably blocking is an attempt to steal the full prompt of the request. Basically, if you managed to &ldquo;reset the session,&rdquo; you could figure out what Cody was including as instructions or as context. The only reason to block such a thing would be to protect their business in some way, as knowing what the prompt contains can be used to compete with them.</p> <p>So yes, I was kind of annoyed that Cody&rsquo;s one job was to include good context, and it included context that broke its own filtering to protect itself.</p> ChaZ: Chaz haz a Zervice https://jackson.dev/post/chaz-haz-a-zervice/ Sat, 26 Oct 2024 00:00:00 +0000 https://jackson.dev/post/chaz-haz-a-zervice/ <p>You&rsquo;ve heard of <a href="https://en.wikipedia.org/wiki/Software_as_a_service" class="external-link" target="_blank" rel="noopener">SaaS</a> and <a href="https://en.wikipedia.org/wiki/Infrastructure_as_a_service" class="external-link" target="_blank" rel="noopener">IaaS</a>, so I wanted to get in on that.</p> <p>But, you know, not make any money from it.</p> <h2 id="background-what-is-chaz"> Background: What is <a href="https://chaz.is" class="external-link" target="_blank" rel="noopener">Chaz</a>? <a class="heading-link" href="#background-what-is-chaz"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I already announced <a href="https://jackson.dev/post/chaz/" class="external-link" target="_blank" rel="noopener">Chaz</a>, but now I&rsquo;ve made <strong>ChaZ</strong>.</p> <p>Chaz is my Matrix bot that hooks your chats up to the LLM of your choice. <a href="https://chaz.is" class="external-link" target="_blank" rel="noopener">Chaz</a> hooks up your normal Matrix client with the LLMs, you can even add images into the conversation if the backend supports it.</p> <p>I hated the fact that all those cute little VC-funded startups were stupidly making their own clients, so I wrote a little bot that competes with them.</p> <h2 id="introducing-chaz"> Introducing ChaZ <a class="heading-link" href="#introducing-chaz"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p><img src="https://jackson.dev/chazhazazervice.jpg" alt="Chaz Haz A Zervice Logo"></p> <p>Chaz haz a Zervice now.</p> <p>I added the ability to setup your own API keys while talking to Chaz, so this lets me run a bot account that anyone can use.</p> <p>The API keys are stored inside Matrix room tags. Those are only accessible to the account that sets them, in this case the Chaz bot.</p> <p>It means that anyone running the Chaz bot can access them, but they don&rsquo;t have to worry about storing anything other than login info locally. Matrix, and the homeserver being used, stores the data itself.</p> <h2 id="but-why"> But Why? <a class="heading-link" href="#but-why"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I realize that a small percentage of people aren&rsquo;t in the habit of running their own services. (/s!)</p> <p>By offering to host it myself, I can allow users who don&rsquo;t have a selfhosted homelab to use this.</p> <h2 id="how-do-i-use-it"> How do I use it? <a class="heading-link" href="#how-do-i-use-it"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>The full info about how to use it is in the <a href="https://github.com/arcuru/chaz" class="external-link" target="_blank" rel="noopener">Chaz README</a>.</p> <p>In brief though, you just add the Chaz account to the room of your choosing and interact using <code>!chaz</code> commands. Send <code>!chaz help</code> to get a list of options.</p> <p>The user interaction is not that mature, but if you have suggestions for improvements please file an issue in the repo.</p> <h3 id="can-i-use-it-with-groups"> Can I use it with groups? <a class="heading-link" href="#can-i-use-it-with-groups"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Yes!</p> <p>Chaz should work &hellip;<em>ok</em>&hellip; for a group context, but it definitely needs more testing and refinement.</p> <p>When adding your own API keys for a group, you should probably send the command to Chaz, <code>!chaz backend &lt;name&gt; &lt;url&gt; &lt;api key&gt;</code>, and once Chaz acks the setup you can delete the message with that data in it. Users in the group will still be able to use the API key through Chaz, but won&rsquo;t have access to the API key themselves.</p> <h3 id="i-dont-trust-you"> I don&rsquo;t trust you <a class="heading-link" href="#i-dont-trust-you"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Ok, then don&rsquo;t use ChaZ, just run your own? I can&rsquo;t see a way where you could bring your own API keys without trusting the bot owner with access to the keys.</p> <h2 id="whats-the-bots-name-again"> What&rsquo;s the bot&rsquo;s name again? <a class="heading-link" href="#whats-the-bots-name-again"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p><a href="https://matrix.to/#/@chaz:jackson.dev" class="external-link" target="_blank" rel="noopener">@chaz:jackson.dev</a></p> Pok'em: Seamless Notifications with Matrix https://jackson.dev/post/pokem/ Fri, 25 Oct 2024 00:00:00 +0000 https://jackson.dev/post/pokem/ <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># These three commands are all equivalent</span> </span></span><span style="display:flex;"><span>pokem !RoomID:jackson.dev Backup failed! </span></span><span style="display:flex;"><span>pokem --room !RoomID:jackson.dev Backup failed! </span></span><span style="display:flex;"><span>curl --fail -d <span style="color:#a6da95">&#34;Backup failed!&#34;</span> pokem.dev/!RoomID:jackson.dev </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>pokem Backup failed! <span style="color:#6e738d;font-style:italic"># Will send to your configured default room</span> </span></span><span style="display:flex;"><span>pokem error Backup failed! <span style="color:#6e738d;font-style:italic"># Will send to your configured room named &#34;error&#34;</span> </span></span><span style="display:flex;"><span>pokem --room error Backup failed! <span style="color:#6e738d;font-style:italic"># Same as above</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># It also accepts stdin as the room message</span> </span></span><span style="display:flex;"><span><span style="color:#91d7e3">echo</span> <span style="color:#a6da95">&#34;Backup failed!&#34;</span> | pokem error <span style="color:#6e738d;font-style:italic"># Sends to the room named &#34;error&#34;</span> </span></span><span style="display:flex;"><span>cat README.md | pokem <span style="color:#6e738d;font-style:italic"># Send the contents of a file to the default room</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Authentication</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Inside Matrix, you can set a pokempassword using the bot interface.</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># If you do that, you won&#39;t be pinged at 3am by some rando, because even if</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># They can guess your roomid, they&#39;ll still need to include the password like so:</span> </span></span><span style="display:flex;"><span>curl --fail pokem.dev/roomid -d <span style="color:#a6da95">&#34;poke the room&#34;</span> -H <span style="color:#a6da95">&#34;Authentication: pokempassword&#34;</span> </span></span><span style="display:flex;"><span>pokem --auth pokempassword --room roomid poke the room </span></span></code></pre></div><p>While writing <a href="https://jackson.dev/post/chaz" class="external-link" target="_blank" rel="noopener">Chaz</a> and <a href="https://github.com/arcuru/headjack" class="external-link" target="_blank" rel="noopener">headjack</a>, I realized that it would be pretty easy to build a self-hosted alternative to <a href="https:///ntfy.sh" class="external-link" target="_blank" rel="noopener">ntfy</a>&hellip;</p> <p>So anyways, that led to building <a href="https://github.com/arcuru/pokem" class="external-link" target="_blank" rel="noopener">Pok&rsquo;em</a> (&ldquo;Poke Them&rdquo;)</p> <p>Pok&rsquo;em is a <a href="https://matrix.org" class="external-link" target="_blank" rel="noopener">Matrix</a> bot, that listens to HTTP requests, and from there pings a Matrix room. It&rsquo;s available as a CLI app to send the notifications, but you can also just use <a href="https://github.com/curl/curl" class="external-link" target="_blank" rel="noopener">curl</a></p> <p>The goal here was just to build something super easy to self-host, while still keeping many of the design elements of ntfy. It supports some ntfy features, but only important ones&hellip;like emoji support.</p> <p>If you don&rsquo;t want to self-host, I also run a hosted bot <a href="https://matrix.to/#/@pokem:jackson.dev" class="external-link" target="_blank" rel="noopener">@pokem:jackson.dev</a>, its public interface is on <a href="https://pokem.dev" class="external-link" target="_blank" rel="noopener">pokem.dev</a></p> <h2 id="key-features"> Key Features <a class="heading-link" href="#key-features"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I added more features than necessary probably:</p> <ol> <li> <p><strong>Command Line and HTTP Interface</strong>: You can notify a Matrix room directly from the Pok&rsquo;em CLI, or with HTTP using PUT/POST requests. So it&rsquo;s easy to integrate in CI or from anywhere.</p> </li> <li> <p><strong>No Service Needed</strong>: The Pok&rsquo;em CLI tool can be used by itself without a server. As long as it has the Matrix login info it will log in and send the notification itself, no Pok&rsquo;em server necessary.</p> </li> <li> <p><strong>GUI Interface</strong>: When viewing with a browser, the service runs an HTTP site that takes in commands, see <a href="https://pokem.dev" class="external-link" target="_blank" rel="noopener">pokem.dev</a></p> </li> <li> <p><strong>Users Control Readers</strong>: Matrix has its own Encryption and access controls. You invite the bot to your room, and you can control who gets access to that room.</p> </li> <li> <p><strong>Users Control Writers</strong>: You can set authentication keys on each room, requiring the sender to include the auth key. That controls who can send messages into your room, so you don&rsquo;t get some rando pinging your on-call room at 3am.</p> </li> <li> <p><strong>Docker Support</strong>: It&rsquo;s on Docker and has a tiny config file, but you also can just use my hosted pokem bot <a href="https://matrix.to/#/@pokem:jackson.dev" class="external-link" target="_blank" rel="noopener">@pokem:jackson.dev</a>.</p> </li> </ol> <p>If you want have questions, post on the repo or the public Matrix room: <a href="https://matrix.to/#/#pokem:jackson.dev" class="external-link" target="_blank" rel="noopener">#pokem:jackson.dev</a>.</p> <p><img src="https://jackson.dev/pokem_example.jpg" alt="Pokem Example Notifications"></p> Cloning Windows Recall in 30 Lines of Bash https://jackson.dev/post/cloning-windows-recall-in-30-lines-of-bash/ Thu, 06 Jun 2024 00:00:00 +0000 https://jackson.dev/post/cloning-windows-recall-in-30-lines-of-bash/ <p>June 6, 2024</p> <p>This is mostly a demonstration of what Windows Recall actually seems to do under the hood, and is not meant to be seriously used.</p> <p>Here&rsquo;s the code, I have more to say after:</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#!/usr/bin/env bash </span></span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$1</span><span style="color:#a6da95">&#34;</span> <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#34;--daemon&#34;</span> <span style="color:#91d7e3;font-weight:bold">]</span>; <span style="color:#c6a0f6">then</span> </span></span><span style="display:flex;"><span> <span style="color:#f4dbd6">MD5LAST</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">&#34;&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">while</span> true; <span style="color:#c6a0f6">do</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Use the current date and time for the screenshot filename</span> </span></span><span style="display:flex;"><span> <span style="color:#f4dbd6">FILE</span><span style="color:#91d7e3;font-weight:bold">=</span>~/recall/<span style="color:#c6a0f6">$(</span>date +<span style="color:#a6da95">&#34;%Y/%m/%d/%Y-%m-%d-%H:%M:%S&#34;</span><span style="color:#c6a0f6">)</span> </span></span><span style="display:flex;"><span> mkdir -p <span style="color:#c6a0f6">$(</span>dirname <span style="color:#f4dbd6">$FILE</span><span style="color:#c6a0f6">)</span> </span></span><span style="display:flex;"><span> grim <span style="color:#f4dbd6">$FILE</span>.png <span style="color:#6e738d;font-style:italic"># Take a screenshot</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># If the screenshot is the same as the last one, disregard it</span> </span></span><span style="display:flex;"><span> <span style="color:#f4dbd6">MD5</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#c6a0f6">$(</span>md5sum <span style="color:#f4dbd6">$FILE</span>.png | cut -d <span style="color:#a6da95">&#39; &#39;</span> -f 1<span style="color:#c6a0f6">)</span> </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$MD5</span><span style="color:#a6da95">&#34;</span> <span style="color:#91d7e3;font-weight:bold">==</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$MD5LAST</span><span style="color:#a6da95">&#34;</span> <span style="color:#91d7e3;font-weight:bold">]</span>; <span style="color:#c6a0f6">then</span> </span></span><span style="display:flex;"><span> rm <span style="color:#f4dbd6">$FILE</span>.png </span></span><span style="display:flex;"><span> sleep <span style="color:#f5a97f">5</span> </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">continue</span> </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">fi</span> </span></span><span style="display:flex;"><span> <span style="color:#f4dbd6">MD5LAST</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f4dbd6">$MD5</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Run tesseract on the file to extract text from the image</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># It always appends .txt to the filename, so the final name is $FILE.ocr.txt</span> </span></span><span style="display:flex;"><span> tesseract <span style="color:#f4dbd6">$FILE</span>.png <span style="color:#f4dbd6">$FILE</span>.ocr &gt; /dev/null 2&gt;&amp;<span style="color:#f5a97f">1</span> &amp; </span></span><span style="display:flex;"><span> sleep <span style="color:#f5a97f">5</span> </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">done</span> </span></span><span style="display:flex;"><span> <span style="color:#91d7e3">exit</span> <span style="color:#f5a97f">0</span> </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fi</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># The arguments are assumed to be a ripgrep search query</span> </span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FILES</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#c6a0f6">$(</span>rg --files-with-matches <span style="color:#f4dbd6">$@</span> ~/recall<span style="color:#c6a0f6">)</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Replace the filename extensions, which should be .ocr.txt, with .png</span> </span></span><span style="display:flex;"><span><span style="color:#f4dbd6">FILES</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#a6da95">${</span><span style="color:#f4dbd6">FILES</span>//.ocr.txt/.png<span style="color:#a6da95">}</span> </span></span><span style="display:flex;"><span>mcomix <span style="color:#f4dbd6">$FILES</span> </span></span></code></pre></div><p>When called with <code>--daemon</code> it will handle the screenshotting/OCR analysis, and when called with anything else it is interpreted as a ripgrep search.</p> <p>Note this has quite a few dependencies, including Wayland since it is using <a href="https://sr.ht/~emersion/grim/" class="external-link" target="_blank" rel="noopener">grim</a> for screenshotting. I would package it up using <a href="https://jackson.dev/post/a-portable-nix-shell-shebang/" class="external-link" target="_blank" rel="noopener">A Portable Nix-shell Shebang</a> if I wanted to distribute it.</p> <h2 id="why"> Why? <a class="heading-link" href="#why"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I admit that I actually <em>like</em> the idea of having Recall available, I just want it to be OSS.</p> <p>I&rsquo;ve been thinking about writing my own version of Recall that could run cross platform, sync between devices, and that I could have full control over outside the whims of Microsoft. I know that <a href="https://rem.ing/" class="external-link" target="_blank" rel="noopener">rem</a> and <a href="https://github.com/jasonjmcghee/xrem" class="external-link" target="_blank" rel="noopener">xrem</a> exist, but I would prefer to write my own greenfield project, and I struggled to get the <code>xrem</code> GUI to work anyways.</p> <p>I would also want to take the project in a slightly different direction.</p> <p>The sheer amount of information that this tool would ingest would make it a good starting point as a centralized &ldquo;Search Everything I&rsquo;ve Used&rdquo; tool, which the other projects don&rsquo;t seem to be doing, and I&rsquo;m tempted to build. For example, giving direct access to index your email/chat/photos/directories/cmd history/etc would allow the tool to also search through every other organized piece of data, and place the screenshots into the right context.</p> <p>Instead of starting a full blown Rust project like I usually do (see <a href="https://github.com/arcuru/pokem" class="external-link" target="_blank" rel="noopener">pokem</a>, <a href="https://github.com/arcuru/chaz" class="external-link" target="_blank" rel="noopener">chaz</a>, <a href="https://github.com/arcuru/cmprss" class="external-link" target="_blank" rel="noopener">cmprss</a>, etc) I wanted to experiment with the tech a bit by writing a simple script to outline what the basic functionality of this thing actually is.</p> <p>So one short coding session later here we have it.</p> <p>Of course this script is barely usable, but it will absolutely work for watching everything that appears on your screen and searching for text that appears.</p> Chaz: An LLM <-> Matrix Chatbot https://jackson.dev/post/chaz/ Mon, 15 Apr 2024 00:00:00 +0000 https://jackson.dev/post/chaz/ <p><a href="https://chaz.is" class="external-link" target="_blank" rel="noopener">Chaz is</a> a <a href="https://matrix.org" class="external-link" target="_blank" rel="noopener">Matrix</a> bot that connects you to a large number of LLM models. It is a replacement for the Chat interfaces that you might be used to when working with LLMs.</p> <p><a href="https://github.com/arcuru/chaz" class="external-link" target="_blank" rel="noopener">Code repository</a></p> <p>Instead of dealing with each company&rsquo;s custom interfaces, this just moves your chats into Matrix with all the features of your favorite Matrix client.</p> <p>(Chaz is written in Rust, and while building this I also built a <em>very</em> basic Matrix Bot Framework in Rust, <a href="https://github.com/arcuru/headjack" class="external-link" target="_blank" rel="noopener">headjack</a>)</p> <p>By plugging into <a href="https://matrix.org" class="external-link" target="_blank" rel="noopener">Matrix</a>, we get a few things for free:</p> <ol> <li>Use any Matrix client you want, and any Matrix tooling since they are just Matrix chats.</li> <li>Let anyone you want use it (there&rsquo;s an allow_list). For example, run a bot yourself and share access to your API keys with your less technical friends. Or access to your internal company models/keys.</li> <li>Share a chat session with anyone. Just add them into the Room.</li> </ol> <p>Originally I wanted to allow users to use their own API keys, so you could run this as a simple service and just point people to it instead of making everyone run this themselves. There are obvious security implications with this though since whoever controlled the bot would have access to the keys, and I don&rsquo;t want to deal with that now.</p> <p>I have setup a demo account if you want to test it out a little, but long term this is something you&rsquo;ll need to run yourself. You&rsquo;ll only be able to send a few messages to the demo account, and you won&rsquo;t be able to use it in large rooms. It&rsquo;s also running a system prompt that is more for amusement than practicality.</p> <p>The demo is @chaz-demo:jackson.dev</p> <p><img src="https://jackson.dev/meet-chaz-demo.png" alt="chaz-demo example conversation"></p> <h2 id="why"> Why <a class="heading-link" href="#why"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>A few weeks ago I was trying to play with different LLMs. Running some locally with ollama or llamafile, using ChatGPT or Gemini, or other APIs like Together.ai or Groq. All of those have different interfaces, either custom made or only available over API.</p> <p>But it&rsquo;s just a Chat interface, and I have already been moving all of my chats into Matrix.</p> <p><a href="https://github.com/sigoden/aichat" class="external-link" target="_blank" rel="noopener">AIChat</a> was the missing piece that tied it together for me. It&rsquo;s a command line interface that lets you setup a lot of different backends and select them on the command line. Pushing ollama and ChatGPT into the same interface.</p> <p>So I had an idea, it&rsquo;d be easy to write a simple Matrix bot, with a mostly text interface, and use AIChat as the backend to unify everything into Matrix.</p> <h3 id="why-is-it-called-chaz"> Why is it called Chaz? <a class="heading-link" href="#why-is-it-called-chaz"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Several months ago I saw someone on Hackernews mention that they use a system prompt requesting that the models refer to themselves as Chaz in the 3rd person. I&rsquo;ve been using it myself and I find it hilarious.</p> <p>I <em>think</em> it was a reference to <a href="https://disenchantment.fandom.com/wiki/Chazzzzz" class="external-link" target="_blank" rel="noopener">Chazzzzz</a> from the show Disenchantment.</p> <h2 id="results"> Results <a class="heading-link" href="#results"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>After a bit of work, I now have <a href="https://github.com/arcuru/chaz" class="external-link" target="_blank" rel="noopener">Chaz</a>. It is pretty rough, but I already use it as a mostly fully capable replacement for the other interfaces. You can:</p> <ol> <li>Send the chats to any backend setup in AIChat.</li> <li>Send images to the image models.</li> <li>Switch between backends in the middle of a session.</li> <li>Rename the Room/Topic using a model.</li> <li>Setup a role/system prompt to whatever you want.</li> </ol> <p>It very quickly became a workable replacement for the Chat interfaces I had been using.</p> <h3 id="matrix-bot-framework-for-rust"> Matrix Bot Framework for Rust <a class="heading-link" href="#matrix-bot-framework-for-rust"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>I wrote this in Rust but there doesn&rsquo;t seem to exist a Matrix bot framework for Rust. So I ended up splitting out <a href="https://github.com/arcuru/headjack" class="external-link" target="_blank" rel="noopener">headjack</a> into it&rsquo;s own project.</p> <p>It handles the bare minimum necessary to deal with the Matrix protocol with a lean towards helping write bots. A very simple bot could be written on top of headjack with minimal effort</p> <h2 id="usage"> Usage <a class="heading-link" href="#usage"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Chaz supports commands (currently everything prefixed with a &lsquo;.&rsquo; to match AIChat, but that may change)</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>.help </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>Available commands: </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">-</span> .print - Print the conversation </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">-</span> .send - &lt;message&gt; - Send this message without context </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">-</span> .model - &lt;model&gt; - Select the model to use </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">-</span> .list - List available models </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">-</span> .clear - Ignore all messages before this point </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">-</span> .rename - Rename the room and set the topic based on the chat content </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">-</span> .help - Show this message </span></span></code></pre></div><p>Any message that isn&rsquo;t a command causes the entire conversation (up to the last <code>.clear</code> command) to be send to AIChat for completion.</p> <h2 id="future"> Future <a class="heading-link" href="#future"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Here are some things I want to do with this, most of which just require some typing and not building anything novel:</p> <ol> <li>Login as your own Matrix account to provide suggested responses to messages. (I&rsquo;m not sure the cleanest way to setup that interface).</li> <li>This one is definitely <em>better</em> when it&rsquo;s builtin to your client, but we may be able to fake this one ok.</li> <li><em>Ideally</em> I&rsquo;d want to be able to edit my drafts from the bot, but I don&rsquo;t think Matrix syncs drafts.</li> <li>e.g. Give it access to your own account, so you can ask Chaz &ldquo;Help me draft a reply in room &lsquo;Fizx Gang&rsquo;, I want to explain XYZ&rdquo;.</li> <li>Possibly use the Room Topic to store the configuration, for display/editing of the settings.</li> <li>Send to multiple backends in a single session.</li> <li>Respond to Reactions (thumbs up/down/etc). Maybe by regenerating responses, selecting the response you want to continue with when using multiple backends, etc.</li> <li>Support both sending and receiving files, to hookup to image generation backends.</li> <li>Replace AIChat as the backend, as using that as the &ldquo;API&rdquo; is fairly limiting.</li> <li>LLamafile support.</li> <li>Support for changing the model settings from the chat. Temperature, Grammar, Tokens requested, etc.</li> </ol> NixOS Escape Hatch https://jackson.dev/post/nixos-escape-hatch/ Wed, 20 Mar 2024 00:00:00 +0000 https://jackson.dev/post/nixos-escape-hatch/ <p>March 20, 2024</p> <p>I&rsquo;ve used NixOS exclusively for several years now, and it&rsquo;s occasionally useful to have an &ldquo;escape hatch&rdquo; into a different distro.</p> <p>For certain tasks NixOS is more difficult than it needs to be, so being able to run a task in Ubuntu/Arch/etc has saved time by not having to deal with the Nix configs.</p> <p>For example, I work with a lot of different software projects, and <em>sometimes</em> I want to use something that isn&rsquo;t trivial to get setup in NixOS. Many projects will have build instructions for Ubuntu, and officially support building only on Ubuntu, and don&rsquo;t yet have a nixpkgs package for NixOS.</p> <p>In those cases figuring out the custom Nix shell environment can be a bit of a pain. And if I just want to jump in and fix a small bug it isn&rsquo;t worth it to go through all that setup.</p> <h2 id="enter-containers"> Enter Containers <a class="heading-link" href="#enter-containers"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Instead of using docker, you can use the already builtin <code>systemd-nspawn</code>.</p> <p>NixOS uses systemd as it&rsquo;s init system, so it&rsquo;s already installed and you just need to set it up. Systemd can manage a full container setup using <code>systemd-nspawn</code> and <code>machinectl</code>.</p> <p>No installing docker if you don&rsquo;t need it. No managing a docker-compose file. Just pull and install an image and you&rsquo;re good.</p> <p>Containers will be saved in <code>/var/lib/machines</code></p> <h2 id="gui"> GUI <a class="heading-link" href="#gui"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>It is possible to use GUI programs from inside the container. I don&rsquo;t use them (yet), but it should be possible by setting the <code>DISPLAY</code> variable in the environment. See the docs on the <a href="https://wiki.archlinux.org/title/systemd-nspawn#Use_an_X_environment" class="external-link" target="_blank" rel="noopener">Arch wiki</a> for some pointers.</p> <p>I will update this if I start using a GUI from the container.</p> <h2 id="nix-config"> Nix Config <a class="heading-link" href="#nix-config"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Here is the Nix code needed for an nspawn container named &ldquo;ubuntu&rdquo;. You should adapt this to any containers that you want to setup, feel free to make more than one. Before using this config you should setup the machine named <code>ubuntu</code> or you&rsquo;ll run into some errors.</p> <p>The code below bind mounts my code directory <code>/home/patrick/code</code>, since I use this environment for working with code projects that I don&rsquo;t want to bother setting up in NixOS. You should adapt this to your system, and include whatever folders you want to share with the container.</p> <p>If you want to see what any of the options do, look them up on <a href="https://search.nixos.org/options" class="external-link" target="_blank" rel="noopener">NixOS Search</a></p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span> systemd<span style="color:#91d7e3;font-weight:bold">.</span>targets<span style="color:#91d7e3;font-weight:bold">.</span>machines<span style="color:#91d7e3;font-weight:bold">.</span>enable <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#eed49f">true</span>; </span></span><span style="display:flex;"><span> systemd<span style="color:#91d7e3;font-weight:bold">.</span>nspawn<span style="color:#91d7e3;font-weight:bold">.</span><span style="color:#a6da95">&#34;ubuntu&#34;</span> <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> enable <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#eed49f">true</span>; </span></span><span style="display:flex;"><span> execConfig <span style="color:#91d7e3;font-weight:bold">=</span> {Boot <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#eed49f">true</span>;}; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> filesConfig <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Bind resolve.conf to get networking</span> </span></span><span style="display:flex;"><span> BindReadOnly <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#34;/etc/resolv.conf:/etc/resolv.conf&#34;</span>]; </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Bind any directories that you want to be shared</span> </span></span><span style="display:flex;"><span> Bind <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#34;/home/patrick/code&#34;</span>]; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> networkConfig <span style="color:#91d7e3;font-weight:bold">=</span> {Private <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#eed49f">false</span>;}; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> systemd<span style="color:#91d7e3;font-weight:bold">.</span>services<span style="color:#91d7e3;font-weight:bold">.</span><span style="color:#a6da95">&#34;systemd-nspawn@ubuntu&#34;</span> <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> enable <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#eed49f">true</span>; </span></span><span style="display:flex;"><span> requiredBy <span style="color:#91d7e3;font-weight:bold">=</span> [<span style="color:#a6da95">&#34;machines.target&#34;</span>]; </span></span><span style="display:flex;"><span> overrideStrategy <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;asDropin&#34;</span>; </span></span><span style="display:flex;"><span> }; </span></span></code></pre></div><p>This sets up a systemd service that will manage the container and start at boot. Use the normal <code>systemctl stop/start/restart</code> commands to enable/disable the container service.</p> <h2 id="installing"> Installing <a class="heading-link" href="#installing"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>To actually setup and use the machine, we&rsquo;ll use <a href="https://www.nspawn.org" class="external-link" target="_blank" rel="noopener">nspawn.org</a>. They host container images for several common distros which makes it very easy to get started. See their <a href="https://nspawn.org/faq/" class="external-link" target="_blank" rel="noopener">FAQ</a> for their instructions, but the short version is that <code>machinectl</code> is what you should use for most everything.</p> <p>NOTE: The command shown here pulls the image <em>without verifying the signature</em>. If you want to be extra cautious, follow the instructions on the nspawn FAQ to setup signature verification</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>&gt; machinectl pull-tar https://hub.nspawn.org/storage/ubuntu/mantic/tar/image.tar.xz ubuntu --verify<span style="color:#91d7e3;font-weight:bold">=</span>no </span></span></code></pre></div><h2 id="manage"> Manage <a class="heading-link" href="#manage"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Using the machine is pretty easy. Start the container and then login with:</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>&gt; machinectl start ubuntu </span></span><span style="display:flex;"><span>&gt; machinectl login ubuntu </span></span></code></pre></div><p>The default root login for the nspawn.org images is <strong>root/root</strong>.</p> <p><code>machinectl</code> has subcommands for stopping, deleting, or cloning the machine. I suggest you read through the options in <code>machinectl --help</code>.</p> <p>One method you could use for it is to setup a base Ubuntu/Arch image and then clone it as needed for testing or specific projects. Or you could just have single long-running machines that you keep around.</p> <p>With root access, I trust you can take it from here, so I won&rsquo;t bore you with any more details.</p> A Portable Nix-shell Shebang https://jackson.dev/post/a-portable-nix-shell-shebang/ Tue, 07 Nov 2023 00:00:00 +0000 https://jackson.dev/post/a-portable-nix-shell-shebang/ <p>Updated May 21, 2025, with a better script example</p> <p>All of my personal scripts with more than a few dependencies start with <a href="https://nixos.wiki/wiki/Nix-shell_shebang" class="external-link" target="_blank" rel="noopener">the Nix-shell shebang</a>: <code>#!/usr/bin/env nix-shell</code>. Regardless of what language they are written in.</p> <p>That only works if nix is installed, so it is difficult to share with others who may not use nix.</p> <p>If I want the script to be shared with other people, I instead use something like this:</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#!/usr/bin/env bash </span></span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Check if we&#39;re already inside a nix-shell; if not, relaunch script inside nix-shell</span> </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> -z <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$INSIDE_NIX_RANDOMSTRING</span><span style="color:#a6da95">&#34;</span> <span style="color:#91d7e3;font-weight:bold">]</span> <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> <span style="color:#91d7e3">command</span> -v nix-shell &amp;&gt;/dev/null; <span style="color:#c6a0f6">then</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Set environment variable to prevent infinite recursion</span> </span></span><span style="display:flex;"><span> <span style="color:#91d7e3">export</span> <span style="color:#f4dbd6">INSIDE_NIX_RANDOMSTRING</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Launch the script within a nix-shell with required dependencies</span> </span></span><span style="display:flex;"><span> <span style="color:#91d7e3">exec</span> nix-shell --packages jq go --run <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$0</span><span style="color:#a6da95"> </span><span style="color:#f4dbd6">$*</span><span style="color:#a6da95">&#34;</span> </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fi</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Verify dependencies are available</span> </span></span><span style="display:flex;"><span>go </span></span><span style="display:flex;"><span>jq --version </span></span></code></pre></div><p>Or the flake version:</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic">#! /usr/bin/env bash </span></span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">[</span> -z <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$INSIDE_NIX_RANDOMSTRING</span><span style="color:#a6da95">&#34;</span> <span style="color:#91d7e3;font-weight:bold">]</span> <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> <span style="color:#91d7e3">command</span> -v nix-shell &amp;&gt;/dev/null; <span style="color:#c6a0f6">then</span> </span></span><span style="display:flex;"><span> <span style="color:#91d7e3">export</span> <span style="color:#f4dbd6">INSIDE_NIX_RANDOMSTRING</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">1</span> </span></span><span style="display:flex;"><span> <span style="color:#91d7e3">exec</span> nix shell nixpkgs#jq nixpkgs#go --command <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$0</span><span style="color:#a6da95">&#34;</span> <span style="color:#a6da95">&#34;</span><span style="color:#f4dbd6">$@</span><span style="color:#a6da95">&#34;</span> </span></span><span style="display:flex;"><span><span style="color:#c6a0f6">fi</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>go </span></span><span style="display:flex;"><span>jq --version </span></span></code></pre></div><p>That snippet checks for the presence of nix-shell, and then reruns the script under nix-shell after installing the necessary dependencies. The <code>-I</code> argument is optional, and can be used to pin nixpkgs to a specific version (using a commit hash).</p> <h2 id="portability"> Portability <a class="heading-link" href="#portability"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>For my personal scripts I just use the normal nix-shell shebang line, <code>#!/usr/bin/env nix-shell</code>, but I can do that because <em>I</em> am the only one that typically uses them, and I use NixOS. If you want to write scripts to share with others, adding Nix as a dependency isn&rsquo;t always going to be a good idea.</p> <p>Instead, what I do above is use it to install dependencies <em>if it&rsquo;s available</em>. That way you can have all the non-Nix folks deal with their dependency nightmares themselves, and it will just work for all the people who have adopted Nix.</p> <p>A similar approach can be adopted if you want to use <a href="https://guix.gnu.org/" class="external-link" target="_blank" rel="noopener">Guix</a> instead of Nix. In fact, I&rsquo;m sure the snippet above could be extended to check for the presence of either. If you know Guix, send me an email with the updated snippet and I&rsquo;ll include it here.</p> <h2 id="when-not-to-use-it"> When Not To Use It <a class="heading-link" href="#when-not-to-use-it"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>One scenario where I don&rsquo;t use this technique is in development repos. Repos tend to end up with a decent amount of helper scripts, and it&rsquo;s tempting to make use of nix-shell to install dependencies for all of those.</p> <p>I don&rsquo;t think you should bother to do that.</p> <p>Instead dependencies should be managed at the repo level, by using a <code>flake.nix</code> or a <code>shell.nix</code> in the root directory. That keeps things a bit simpler and ensures that all the dependencies are installed in one place, and with the same versions.</p> Nix: Reasonable Default Configs https://jackson.dev/post/nix-reasonable-defaults/ Wed, 30 Nov 2022 00:00:00 +0000 https://jackson.dev/post/nix-reasonable-defaults/ <p>Updated October 3, 2024</p> <p><strong>Updated with help from <a href="https://poz.pet" class="external-link" target="_blank" rel="noopener">@poz</a> and others who have reached out with suggestions.</strong></p> <p>I&rsquo;ve now been using <a href="https://nixos.org/" class="external-link" target="_blank" rel="noopener">Nix</a>, the package manager, fulltime on my personal machines since 2021. I run NixOS on my personal desktop/laptop and VMs, so I have some reasonable experience using it.</p> <p>I really enjoy using Nix, and think it&rsquo;s a very useful tool, but it definitely needs a lot of tweaking.</p> <p>Today I want to share some of the config settings in Nix that most users probably want to change. The defaults that Nix uses for these settings don&rsquo;t make that much sense to me, and most people who actually use Nix should probably consider changing them.</p> <p>Just a note, Nix had a longstanding bug where it displayed the <a href="https://github.com/NixOS/nix/pull/7190" class="external-link" target="_blank" rel="noopener">wrong default values</a> in the manual, so make sure you are looking at the <a href="https://nixos.org/manual/nix/unstable/command-ref/conf-file.html" class="external-link" target="_blank" rel="noopener">latest unstable Nix manual</a>.</p> <p>I&rsquo;ll go into them in detail below, but here are the settings I want to talk about. I&rsquo;ve grouped them into tiers; Almost always set, Set if understood, and Set for developers.</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span># Almost always set </span></span><span style="display:flex;"><span>connect-timeout = 5 </span></span><span style="display:flex;"><span># log-lines = 25 # Updated: This is now the default! </span></span><span style="display:flex;"><span>min-free = 128000000 </span></span><span style="display:flex;"><span>max-free = 1000000000 </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span># Set if understood </span></span><span style="display:flex;"><span>experimental-features = nix-command flakes </span></span><span style="display:flex;"><span>fallback = true </span></span><span style="display:flex;"><span>warn-dirty = false </span></span><span style="display:flex;"><span>auto-optimise-store = true </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span># Set for developers </span></span><span style="display:flex;"><span>keep-outputs = true </span></span></code></pre></div><h3 id="almost-always-set"> Almost always set <a class="heading-link" href="#almost-always-set"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>These are settings that I think are no-brainers to change for just about everyone, and they should probably just be the default values.</p> <h4 id="connect-timeout"> connect-timeout <a class="heading-link" href="#connect-timeout"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h4> <blockquote> <p>The timeout (in seconds) for establishing connections in the binary cache substituter. It corresponds to curl’s &ndash;connect-timeout option. A value of 0 means no limit.</p> </blockquote> <ul> <li>Default: 0</li> <li>Reasonable: 5</li> </ul> <p>The default of 0 is equal to a timeout of <a href="https://curl.se/libcurl/c/CURLOPT_CONNECTTIMEOUT.html" class="external-link" target="_blank" rel="noopener">300 seconds</a>. This seems pretty unreasonable for most users.</p> <p>This is especially important if you have multiple caches setup, as any offline caches will just hang for this whole time. Assuming you&rsquo;ve also set <code>fallback = true</code>, then setting <code>connect-timeout</code> to a reasonable value will let you have binary caches on your local machines that are only occasionally online.</p> <h4 id="log-lines"> log-lines <a class="heading-link" href="#log-lines"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h4> <p><strong>Update:</strong> The default is now 25 in Nix 2.20, so this is no longer necessary.</p> <blockquote> <p>The number of lines of the tail of the log to show if a build fails.</p> </blockquote> <ul> <li>Default: 10</li> <li>Reasonable: 25 <strong>This is the new default!</strong></li> </ul> <p>These log lines are only shown on a failed build, and I&rsquo;ve found that usually the last 10 lines aren&rsquo;t useful for figuring out what actually failed. Setting it to something even a little bit larger is often enough to debug the build.</p> <p>You can also set this to something very large to output the whole build log.</p> <h4 id="min-freemax-free"> min-free/max-free <a class="heading-link" href="#min-freemax-free"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h4> <blockquote> <p>When free disk space in /nix/store drops below min-free during a build, Nix performs a garbage-collection until max-free bytes are available or there is no more garbage. A value of 0 (the default) disables this feature.</p> </blockquote> <ul> <li>min-free <ul> <li>Default: 0</li> <li>Reasonable: 128000000 (128 MB)</li> </ul> </li> <li>max-free <ul> <li>Default: -1</li> <li>Reasonable: 1000000000 (1 GB)</li> </ul> </li> </ul> <p><strong>Note:</strong> These are actually most useful if you setup <code>/nix</code> on it&rsquo;s own partition, as long as you give it enough space then you never need to manually garbage collect.</p> <p>Nix has a tendency to use a lot of space, especially if you are developing with it. By default nothing will be deleted unless you manually collect garbage with <code>nix-collect-garbage</code>.</p> <p>By setting <code>min-free</code> to a reasonable small value, you can ensure that it doesn&rsquo;t fully fill the partition, which can cause problems if <code>/nix/store</code> is on the same partition as the rest of your system.</p> <p>Users will probably want to tweak these settings themselves, but changing these settings from the defaults will at least ensure that they don&rsquo;t run out of disk-space.</p> <h3 id="set-if-understood"> Set if understood <a class="heading-link" href="#set-if-understood"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>These settings are things that most users should <em>probably</em> set, but you should definitely understand them first.</p> <h4 id="experimental-features--flakes"> experimental-features = flakes <a class="heading-link" href="#experimental-features--flakes"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h4> <ul> <li>Default:</li> <li>Reasonable: nix-command flakes</li> </ul> <p>This enables the technically experimental feature <a href="https://nixos.wiki/wiki/Flakes" class="external-link" target="_blank" rel="noopener">Flakes</a>.</p> <p>Most development with Nix recently is using flakes, and as a user you probably want to enable them. If you are a new user especially I&rsquo;d suggest just using the flakes interface instead of the legacy one.</p> <p>The current status is that there is an in-progress <a href="https://github.com/NixOS/rfcs/pull/136" class="external-link" target="_blank" rel="noopener">plan for a plan to stabilize flakes</a>, though there are still lots of very legitimate concerns about them. I&rsquo;m especially concerned about how they&rsquo;ve handled the <a href="https://github.com/NixOS/flake-registry/issues/25" class="external-link" target="_blank" rel="noopener">global registry</a>. If possible I&rsquo;d suggest you disable the global registry as well, but that&rsquo;s up to you.</p> <p>Even with the drawbacks though, IMO flakes are just clearly a better interface to use Nix with, and they&rsquo;ve been in broad use for several years now.</p> <h4 id="fallback"> fallback <a class="heading-link" href="#fallback"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h4> <blockquote> <p>If set to true, Nix will fall back to building from source if a binary substitute fails. This is equivalent to the &ndash;fallback flag. The default is false.</p> </blockquote> <ul> <li>Default: false</li> <li>Reasonable: true</li> </ul> <p>That description is sort of a lie. It&rsquo;s somewhat common for users to have multiple substituters (caches) setup, either for sharing builds within a company/project or just for caching downloads in your local network. Nix makes it pretty easy to do this on your own machines, especially if they run NixOS, <a href="https://nixos.wiki/wiki/Binary_Cache" class="external-link" target="_blank" rel="noopener">see the wiki</a>.</p> <p>This setting, if set to <code>false</code>, will cause the build to fail if <em>any</em> of the caches are unavailable. That seems pretty nonsensical, and seems like a common complaint, so I opened a <a href="https://github.com/NixOS/nix/pull/7188" class="external-link" target="_blank" rel="noopener">PR to fix it</a>.</p> <p>Even after that fix goes in, I expect that the way most people expect Nix to work is with this set to <code>true</code>. It is literally setup to have reproducible builds, so failing when caches aren&rsquo;t available doesn&rsquo;t make sense in most cases.</p> <p>Some people may want this set to false, especially on slower devices, but you probably want this set to true.</p> <h4 id="warn-dirty"> warn-dirty <a class="heading-link" href="#warn-dirty"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h4> <blockquote> <p>Whether to warn about dirty Git/Mercurial trees.</p> </blockquote> <ul> <li>Default: true</li> <li>Reasonable: false</li> </ul> <p>I don&rsquo;t disagree with the default here, but most people probably don&rsquo;t need these warnings.</p> <h4 id="auto-optimise-store"> auto-optimise-store <a class="heading-link" href="#auto-optimise-store"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h4> <blockquote> <p>If set to true, Nix automatically detects files in the store that have identical contents, and replaces them with hard links to a single copy. This saves disk space. If set to false (the default), you can still run nix-store &ndash;optimise to get rid of duplicate files.</p> </blockquote> <ul> <li>Default: false</li> <li>Reasonable: true</li> </ul> <p>This can save quite a lot of disk space, especially if you upgrade frequently and develop on the machine. I always recommend it, but it does make the layout of <code>/nix/store</code> a little confusing for some people.</p> <h3 id="set-for-developers"> Set for developers <a class="heading-link" href="#set-for-developers"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <h4 id="keep-outputs"> keep-outputs <a class="heading-link" href="#keep-outputs"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h4> <blockquote> <p>If true, the garbage collector will keep the outputs of non-garbage derivations. If false (default), outputs will be deleted unless they are GC roots themselves (or reachable from other roots). In general, outputs must be registered as roots separately. However, even if the output of a derivation is registered as a root, the collector will still delete store paths that are used only at build time (e.g., the C compiler, or source tarballs downloaded from the network). To prevent it from doing so, set this option to true.</p> </blockquote> <ul> <li>Default: false</li> <li>Reasonable: true</li> </ul> <p>This ensures that packages needed for building other packages are kept in the store. If you&rsquo;re going to be building packages locally, this is very useful to prevent having to redownload things a lot.</p> <p>Keep in mind that this will cause your nix store to get pretty large.</p> Replacing Tailscale with a NixOS Module https://jackson.dev/post/replacing-tailscale-with-nixos/ Fri, 28 Oct 2022 00:00:00 +0000 https://jackson.dev/post/replacing-tailscale-with-nixos/ <p>October 28, 2022</p> <p>After using <a href="https://tailscale.com/" class="external-link" target="_blank" rel="noopener">Tailscale</a> to manage my Wireguard VPN for the past few months I decided to migrate my network off of it to my own custom configuration. I found that I didn&rsquo;t need any of the extra features that Tailscale enables, and I enjoyed learning how to manage everything myself.</p> <p>With NixOS, I was able to recreate the ability to do Endpoint migration to make things a little nicer than a normal barebones setup. My NixOS module lets me configure more than 1 Endpoint per peer and will migrate the connections between them as needed. I only really use this to migrate to/from my Private LAN.</p> <p>It should go without saying, but <strong>Tailscale is (probably) free for you to use. You should probably use that if you don&rsquo;t want to mess with network settings.</strong></p> <h2 id="what-is-wireguard"> What is Wireguard? <a class="heading-link" href="#what-is-wireguard"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Wireguard is a barebones VPN protocol that allows you to create secure connections between machines. It uses a public/private keypair setup similar to ssh, requiring that all the peers you connect to have the public keys of their peers. Each node in the network needs to be configured with a few different things (this is not an exhaustive list, but are the most common options):</p> <ol> <li>An IP address</li> <li>A Public/Private keypair</li> <li>A port to use for wireguard connections</li> <li>A list of Peers that can connect, each containing: <ol> <li>The peers Public Key</li> <li>A list of allowed ips that peer is able to send as</li> <li>Optionally, an Endpoint address. An IP address/port combination where the peer is accessible over the public network.</li> </ol> </li> </ol> <p>You can also specify an address for DNS and PostUp/PostDown scripts for making any routing changes when the network goes Up/Down.</p> <p>Wireguard is relatively simple to configure for a static network, requiring only a few pieces of information and not much else. The downside is that it is complicated to make any updates. Changes may have to be mirrored across all devices in the network.</p> <p>Discovery can also be a problem. The peer must be discoverable using the defined Endpoint for the peer, otherwise the connection may not be available. Typically that means that all of the devices you want to connect to need to have a stable IP:port where they can be reached.</p> <p>Alternately, you can route from Peer to Peer by using a server that both are connected to. So long as clients can connect to the same Wireguard server, they can route to each other if necessary by going through the server, though the server and the clients need to be configured correctly to make that happen.</p> <p>The other downside is that only 1 Endpoint can be defined for each peer. The Endpoint is where Wireguard will look for the peer on the public internet to make a connection. For most home users, a peer will often have a public Endpoint (your home IP address + the port you&rsquo;ve forwarded from your router) and a private Endpoint (the local IP address on the lan), and maybe more. Wireguard has no built-in mechanism for checking multiple Endpoints, or migrating between them.</p> <h2 id="what-is-tailscale"> What is Tailscale? <a class="heading-link" href="#what-is-tailscale"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Tailscale manages all the Wireguard problems for you. It handles traversing NATs so that all your peers are reachable, it manages sharing of the public keys among all your devices, and it has a number of nice ease-of-use features built-in to the network (like DNS).</p> <p>For most people, and especially for large deployments, Tailscale just works. There is even a third-party OSS implementation of their control server, <a href="https://github.com/juanfont/headscale" class="external-link" target="_blank" rel="noopener">Headscale</a>, if you&rsquo;d rather self-host and get most of the features.</p> <p>The vast majority of people should just install Tailscale and be done with it.</p> <p>For my personal use case though I had a few issues.</p> <ol> <li><a href="https://github.com/tailscale/tailscale/issues/2880" class="external-link" target="_blank" rel="noopener">It doesn&rsquo;t play nicely with my &ldquo;VPN&rdquo; of choice, Mullvad, even though it probably could.</a></li> <li>You must use a third-party identity provider to login, so it makes my VPN dependent on my Google account.</li> <li>The VPN setup becomes dependent on a Tailscale server to work. I don&rsquo;t want to be unable to connect to my VPN if Tailscale is down.</li> <li>I like having bespoke IP addresses which you can&rsquo;t get with Tailscale.</li> </ol> <h2 id="manually-configuring-wireguard"> Manually Configuring Wireguard <a class="heading-link" href="#manually-configuring-wireguard"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Because of my concerns with using Tailscale and my desire to make things more complicated than necessary, I migrated my VPN over to a custom setup using a mix of manual management and a NixOS module.</p> <p>This is only reasonable for me to do because:</p> <ol> <li>I have a small, static network of only a handful of servers and clients.</li> <li>My home network has a static IP address, and I can configure port forwarding easily.</li> <li>I don&rsquo;t have a need to share access to one of these servers with other people, certainly not someone who would also be using Tailscale.</li> <li>Effectively, I only connect client -&gt; server and never client -&gt; client, which simplifies the setup even more.</li> <li>Most of my devices run NixOS, so I can turn this from a manual management problem into a software engineering problem.</li> </ol> <p>This has been done and written about before. Most notably, Xe&rsquo;s post <a href="https://xeiaso.net/blog/my-wireguard-setup-2021-02-06" class="external-link" target="_blank" rel="noopener">My Automagic NixOS Wireguard Setup</a>. They have since migrated into using Tailscale, since their network became a bit more complex and they wanted some of the niceties that Tailscale offers.</p> <p>By moving the setup into a NixOS module, I can update the network by making changes in one place, and then pushing the update to all my NixOS machines. I do have to manually manage the config for my NAS, which is not on NixOS, but updating 2 different places turns out to be fairly manageable.</p> <p>Instead of just configuring Wireguard, this module also sets up a systemd service that will migrate connections to my Private LAN. A few of my Wireguard servers, my NAS and my Desktop, sit on my local network, and I want clients to use either the Public IP:Port associated with them, or use their Private IP:Port when on the local network. This is mostly pointless, since I can still connect to them with the Public IP:Port from my local network, but since I stream a lot of data between those devices it&rsquo;s better to avoid a hop to/from my ISP that would be required if I routed the packets to my Public IP.</p> <p>I do still occasionally have some issues with running both Mullvad and this private network together, particularly when trying to SSH between machines outside of the local network. Unlike when I was using Tailscale, restarting the connection has always fixed the problem which I suspect is something going wrong with the <code>iptables</code> routing.</p> <h3 id="nixos-module"> NixOS Module <a class="heading-link" href="#nixos-module"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Here&rsquo;s the code for my NixOS module. It is heavily commented, and to use it you should copy this into your config and customize it for your own network.</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-gdscript3" data-lang="gdscript3"><span style="display:flex;"><span>{ config, lib, pkgs, <span style="color:#91d7e3;font-weight:bold">...</span> }: </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Wgnet network</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># This contains the network config for my local LAN running over wireguard</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># 123.45.67.89 - Fake Home IP address</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># 10.41.0.0/16 - LAN addresses with static mapping</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># 10.42.0.0/16 - Addresses in the Virtual Wireguard Network</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Adding a new device:</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># MANUAL: Create wireguard key(s)</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># Each peer needs to have a public/private keypair created. That can be done with</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># the following commands run as root:</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># &gt; umask 077</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># &gt; wg genkey | tee /var/keys/wgnet.priv.key | wg pubkey &gt; /var/keys/wgnet.pub.key</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># &gt; chmod 0644 /var/keys/wgnet.pub.key</span> </span></span><span style="display:flex;"><span><span style="color:#6e738d;font-style:italic"># MANUAL: Setup port forwarding on router for &#39;server&#39; devices to accept incoming connections</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span>with lib; </span></span><span style="display:flex;"><span>let </span></span><span style="display:flex;"><span> cfg <span style="color:#91d7e3;font-weight:bold">=</span> config<span style="color:#91d7e3;font-weight:bold">.</span>modules<span style="color:#91d7e3;font-weight:bold">.</span>services<span style="color:#91d7e3;font-weight:bold">.</span>wgnet; </span></span><span style="display:flex;"><span> homeIP <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;123.45.67.89&#34;</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Define the manual network map based on the host names.</span> </span></span><span style="display:flex;"><span> address <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> desktop <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.42.1.1&#34;</span>; </span></span><span style="display:flex;"><span> laptop <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.42.1.2&#34;</span>; </span></span><span style="display:flex;"><span> vps <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.42.1.3&#34;</span>; </span></span><span style="display:flex;"><span> nas <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.42.1.4&#34;</span>; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Define all the peers in the network</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Each peer requires</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># hostname: The hostname, mapped in the hosts file</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># altHostname: Optional, also mapped in the hosts file)</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># wgnetAddress: IP to use in the VPN</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># publicKey: Wireguard public key</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># publicEndpoint: Optional, publicly addressable location</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># privateEndpoint: Optional, private address that&#39;s not always routable</span> </span></span><span style="display:flex;"><span> devices <span style="color:#91d7e3;font-weight:bold">=</span> [ </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> hostname <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;desktop&#34;</span>; </span></span><span style="display:flex;"><span> wgnetAddress <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.42.1.1&#34;</span>; </span></span><span style="display:flex;"><span> publicEndpoint <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> ip <span style="color:#91d7e3;font-weight:bold">=</span> homeIP; </span></span><span style="display:flex;"><span> port <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;54444&#34;</span>; <span style="color:#6e738d;font-style:italic"># Forwarded via router</span> </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> privateEndpoint <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> ip <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.41.1.1&#34;</span>; </span></span><span style="display:flex;"><span> port <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;53333&#34;</span>; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> publicKey <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&#34;</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> { </span></span><span style="display:flex;"><span> hostname <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;laptop&#34;</span>; </span></span><span style="display:flex;"><span> wgnetAddress <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.42.1.2&#34;</span>; </span></span><span style="display:flex;"><span> privateEndpoint <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> ip <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.41.1.2&#34;</span>; </span></span><span style="display:flex;"><span> port <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;53333&#34;</span>; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> publicKey <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB&#34;</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> { <span style="color:#6e738d;font-style:italic"># Virtual Private Server on AWS</span> </span></span><span style="display:flex;"><span> hostname <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;vps&#34;</span>; </span></span><span style="display:flex;"><span> wgnetAddress <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.42.1.3&#34;</span>; </span></span><span style="display:flex;"><span> publicEndpoint <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> ip <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;98.76.65.43&#34;</span>; </span></span><span style="display:flex;"><span> port <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;53333&#34;</span>; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> publicKey <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC&#34;</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> { <span style="color:#6e738d;font-style:italic"># Home NAS in my local network</span> </span></span><span style="display:flex;"><span> hostname <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;nas&#34;</span>; </span></span><span style="display:flex;"><span> wgnetAddress <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.42.1.4&#34;</span>; </span></span><span style="display:flex;"><span> publicEndpoint <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> ip <span style="color:#91d7e3;font-weight:bold">=</span> homeIP; </span></span><span style="display:flex;"><span> port <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;54445&#34;</span>; <span style="color:#6e738d;font-style:italic"># Forwarded via router</span> </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> privateEndpoint <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> ip <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.41.1.4&#34;</span>; </span></span><span style="display:flex;"><span> port <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;53333&#34;</span>; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> publicKey <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC&#34;</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> ]; </span></span><span style="display:flex;"><span><span style="color:#91d7e3;font-weight:bold">in</span> { </span></span><span style="display:flex;"><span> options<span style="color:#91d7e3;font-weight:bold">.</span>modules<span style="color:#91d7e3;font-weight:bold">.</span>services<span style="color:#91d7e3;font-weight:bold">.</span>wgnet <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> enable <span style="color:#91d7e3;font-weight:bold">=</span> mkEnableOption <span style="color:#a6da95">&#34;wgnet domain&#34;</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> address <span style="color:#91d7e3;font-weight:bold">=</span> mkOption { </span></span><span style="display:flex;"><span> type <span style="color:#91d7e3;font-weight:bold">=</span> types<span style="color:#91d7e3;font-weight:bold">.</span>str; </span></span><span style="display:flex;"><span> example <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;10.42.1.1&#34;</span>; </span></span><span style="display:flex;"><span> default <span style="color:#91d7e3;font-weight:bold">=</span> address<span style="color:#91d7e3;font-weight:bold">.$</span>{config<span style="color:#91d7e3;font-weight:bold">.</span>networking<span style="color:#91d7e3;font-weight:bold">.</span>hostName}; </span></span><span style="display:flex;"><span> description <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;Address of device on the private network.&#34;</span>; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> migrate <span style="color:#91d7e3;font-weight:bold">=</span> mkOption { </span></span><span style="display:flex;"><span> type <span style="color:#91d7e3;font-weight:bold">=</span> types<span style="color:#91d7e3;font-weight:bold">.</span>bool; </span></span><span style="display:flex;"><span> default <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">true</span>; </span></span><span style="display:flex;"><span> description <span style="color:#91d7e3;font-weight:bold">=</span> </span></span><span style="display:flex;"><span> <span style="color:#a6da95">&#34;Run a background service to migrate public -&gt; private endpoints.&#34;</span>; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> config <span style="color:#91d7e3;font-weight:bold">=</span> mkIf cfg<span style="color:#91d7e3;font-weight:bold">.</span>enable { </span></span><span style="display:flex;"><span> networking <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Map the hostnames for easy addressing, both as &#34;hostname&#34; and &#34;hostname.wgnet&#34;</span> </span></span><span style="display:flex;"><span> hosts <span style="color:#91d7e3;font-weight:bold">=</span> listToAttrs (map (x: { </span></span><span style="display:flex;"><span> name <span style="color:#91d7e3;font-weight:bold">=</span> x<span style="color:#91d7e3;font-weight:bold">.</span>wgnetAddress; </span></span><span style="display:flex;"><span> value <span style="color:#91d7e3;font-weight:bold">=</span> [ <span style="color:#a6da95">&#34;${x.hostname}&#34;</span> <span style="color:#a6da95">&#34;${x.hostname}.wgnet&#34;</span> ] </span></span><span style="display:flex;"><span> <span style="color:#91d7e3;font-weight:bold">++</span> (<span style="color:#c6a0f6">if</span> (x <span style="color:#ed8796">?</span> altHostname) then [ </span></span><span style="display:flex;"><span> <span style="color:#a6da95">&#34;${x.altHostname}&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#a6da95">&#34;${x.altHostname}.wgnet&#34;</span> </span></span><span style="display:flex;"><span> ] <span style="color:#c6a0f6">else</span> </span></span><span style="display:flex;"><span> [ ]); </span></span><span style="display:flex;"><span> }) devices); </span></span><span style="display:flex;"><span> firewall <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> allowedUDPPorts <span style="color:#91d7e3;font-weight:bold">=</span> [ <span style="color:#f5a97f">53333</span> ]; </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Force this to false because wg-quick and mullvad don&#39;t play nice with each other.</span> </span></span><span style="display:flex;"><span> checkReversePath <span style="color:#91d7e3;font-weight:bold">=</span> lib<span style="color:#91d7e3;font-weight:bold">.</span>mkForce <span style="color:#91d7e3">false</span>; </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Trust all traffic on this interface</span> </span></span><span style="display:flex;"><span> trustedInterfaces <span style="color:#91d7e3;font-weight:bold">=</span> [ <span style="color:#a6da95">&#34;wgnet&#34;</span> ]; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> wg<span style="color:#91d7e3;font-weight:bold">-</span>quick<span style="color:#91d7e3;font-weight:bold">.</span>interfaces<span style="color:#91d7e3;font-weight:bold">.</span>wgnet <span style="color:#91d7e3;font-weight:bold">=</span> { </span></span><span style="display:flex;"><span> privateKeyFile <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;/var/keys/wgnet.priv.key&#34;</span>; </span></span><span style="display:flex;"><span> address <span style="color:#91d7e3;font-weight:bold">=</span> [ <span style="color:#a6da95">&#34;${cfg.address}/16&#34;</span> ]; </span></span><span style="display:flex;"><span> listenPort <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">53333</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># There is no problem with having a peer device listed for itself, it is ignored.</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Endpoints are a hint as to were to find the device, but connections can be accepted from anywhere.</span> </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Maps the devices list to configured Wireguard peers.</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># It adds an endpoint if that device has a public endpoint, which</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># should be the case for all servers.</span> </span></span><span style="display:flex;"><span> peers <span style="color:#91d7e3;font-weight:bold">=</span> map (x: { </span></span><span style="display:flex;"><span> inherit (x) publicKey; </span></span><span style="display:flex;"><span> allowedIPs <span style="color:#91d7e3;font-weight:bold">=</span> [ <span style="color:#a6da95">&#34;${x.wgnetAddress}/32&#34;</span> ]; </span></span><span style="display:flex;"><span> endpoint <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#c6a0f6">if</span> (x <span style="color:#ed8796">?</span> publicEndpoint) then </span></span><span style="display:flex;"><span> <span style="color:#a6da95">&#34;${x.publicEndpoint.ip}:${x.publicEndpoint.port}&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">else</span> </span></span><span style="display:flex;"><span> null; </span></span><span style="display:flex;"><span> }) devices; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># This script runs manually to move wireguard connections to the LAN if applicable.</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># This is necessary because we want the roaming devices to know how to connect back using the homeIP, but also to use to prefer the private IPs.</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Wireguard doesn&#39;t support listing multiple endpoints for devices, which would be a better fix.</span> </span></span><span style="display:flex;"><span> systemd<span style="color:#91d7e3;font-weight:bold">.</span>services<span style="color:#91d7e3;font-weight:bold">.</span>wgnet<span style="color:#91d7e3;font-weight:bold">-</span>to<span style="color:#91d7e3;font-weight:bold">-</span>lan <span style="color:#91d7e3;font-weight:bold">=</span> mkIf cfg<span style="color:#91d7e3;font-weight:bold">.</span>migrate { </span></span><span style="display:flex;"><span> description <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;Attempt to migrate wireguard connections to LAN.&#34;</span>; </span></span><span style="display:flex;"><span> wantedBy <span style="color:#91d7e3;font-weight:bold">=</span> [ <span style="color:#a6da95">&#34;wg-quick-wgnet.service&#34;</span> ]; </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Run every 5 minutes</span> </span></span><span style="display:flex;"><span> startAt <span style="color:#91d7e3;font-weight:bold">=</span> [ <span style="color:#a6da95">&#34;*:0/5&#34;</span> ]; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Check all peers with both a private and public endpoint and attempt to migrate them.</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Script resets the endpoint iff</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># 1. We can see the device locally and through the wireguard tunnel</span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># 2. The wireguard tunnel doesn&#39;t already see it locally</span> </span></span><span style="display:flex;"><span> script <span style="color:#91d7e3;font-weight:bold">=</span> let </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic"># Setup shorthand links for readability</span> </span></span><span style="display:flex;"><span> wg <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;${pkgs.wireguard-tools}/bin/wg&#34;</span>; </span></span><span style="display:flex;"><span> grep <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;${pkgs.gnugrep}/bin/grep&#34;</span>; </span></span><span style="display:flex;"><span> ping <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#a6da95">&#34;${pkgs.unixtools.ping}/bin/ping&#34;</span>; </span></span><span style="display:flex;"><span> <span style="color:#91d7e3;font-weight:bold">in</span> concatStringsSep <span style="color:#a6da95">&#34;</span><span style="color:#8aadf4">\n</span><span style="color:#a6da95">&#34;</span> (map (x: </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">if</span> (x<span style="color:#91d7e3;font-weight:bold">.</span>hostname <span style="color:#91d7e3;font-weight:bold">!=</span> <span style="color:#a6da95">&#34;${config.networking.hostName}&#34;</span> <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> x <span style="color:#ed8796">?</span> publicEndpoint </span></span><span style="display:flex;"><span> <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> x <span style="color:#ed8796">?</span> privateEndpoint) then <span style="color:#a6da95">&#39;&#39;</span> </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">!</span> <span style="color:#91d7e3;font-weight:bold">$</span>{wg} show wgnet endpoints <span style="color:#91d7e3;font-weight:bold">|</span> <span style="color:#91d7e3;font-weight:bold">$</span>{grep} <span style="color:#91d7e3;font-weight:bold">-</span>q <span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>privateEndpoint<span style="color:#91d7e3;font-weight:bold">.</span>ip}; then </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">$</span>{ping} <span style="color:#91d7e3;font-weight:bold">-</span>c1 <span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>privateEndpoint<span style="color:#91d7e3;font-weight:bold">.</span>ip} <span style="color:#91d7e3;font-weight:bold">&amp;&amp;</span> <span style="color:#91d7e3;font-weight:bold">$</span>{ping} <span style="color:#91d7e3;font-weight:bold">-</span>c1 <span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>wgnetAddress}; then </span></span><span style="display:flex;"><span> echo <span style="color:#a6da95">&#34;Attempting to reset peer ${x.hostname} endpoint to ${x.privateEndpoint.ip}&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#91d7e3;font-weight:bold">$</span>{wg} set wgnet peer <span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>publicKey} endpoint <span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>privateEndpoint<span style="color:#91d7e3;font-weight:bold">.</span>ip}:<span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>privateEndpoint<span style="color:#91d7e3;font-weight:bold">.</span>port} </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">if</span> <span style="color:#91d7e3;font-weight:bold">!</span> <span style="color:#91d7e3;font-weight:bold">$</span>{ping} <span style="color:#91d7e3;font-weight:bold">-</span>c1 <span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>wgnetAddress}; then </span></span><span style="display:flex;"><span> echo <span style="color:#a6da95">&#34;Failed to reset peer ${x.hostname} to ${x.privateEndpoint.ip}. Reverting to ${x.publicEndpoint.ip}.&#34;</span> </span></span><span style="display:flex;"><span> <span style="color:#91d7e3;font-weight:bold">$</span>{wg} set wgnet peer <span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>publicKey} endpoint <span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>publicEndpoint<span style="color:#91d7e3;font-weight:bold">.</span>ip}:<span style="color:#91d7e3;font-weight:bold">$</span>{x<span style="color:#91d7e3;font-weight:bold">.</span>publicEndpoint<span style="color:#91d7e3;font-weight:bold">.</span>port} </span></span><span style="display:flex;"><span> fi </span></span><span style="display:flex;"><span> fi </span></span><span style="display:flex;"><span> fi </span></span><span style="display:flex;"><span> <span style="color:#a6da95">&#39;&#39;</span> <span style="color:#c6a0f6">else</span> </span></span><span style="display:flex;"><span> <span style="color:#a6da95">&#34;&#34;</span>) devices); </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span> }; </span></span><span style="display:flex;"><span>} </span></span></code></pre></div><h2 id="other-thoughts"> Other Thoughts <a class="heading-link" href="#other-thoughts"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Having worked with this quite a bit, I think there is space for an alternative piece of VPN management software. Rather than using a control server like Tailscale I think it would make sense to use something akin to <a href="https://syncthing.net/" class="external-link" target="_blank" rel="noopener">Syncthing.</a> Syncthing, a P2P folder sync service, solves many of the same problems that Wireguard also needs to fix; device discovery and network traversal. I doubt there&rsquo;s enough demand to justify the work needed to write such a thing, but I&rsquo;d probably use it if it existed.</p> Rust Coreutils: Fixing Low-Hanging Performance Fruit https://jackson.dev/post/rust-coreutils-dd/ Tue, 13 Sep 2022 00:00:00 +0000 https://jackson.dev/post/rust-coreutils-dd/ <p>September 13, 2022</p> <p>I have been toying with learning Rust for the last few years, but since I wasn&rsquo;t able to use it at work it never really stuck with me. Throughout my career I mainly worked on low-latency C/C++ systems, and on performance engineering work directly (benchmarking, optimization work, etc) and Rust is trying to play in the same ballpark. Since retiring at the start of this year I&rsquo;ve had a little more free time, so I&rsquo;ve been spending more time writing Rust code for my personal projects and trying to contribute to some Rust OSS projects to get a feel for the language.</p> <p>A few months ago I took a look at the <a href="https://github.com/uutils/coreutils" class="external-link" target="_blank" rel="noopener">uutils coreutils</a> project, which is a rewrite of the core GNU utils in Rust, to try to get some experience writing newbie code in the language. I saw the maintainers themselves mention that a lot of the code quality isn&rsquo;t great since a lot of contributions are from people who are very new to Rust, so I figured there would be some easy fixes available for me to take on.</p> <p>I ended up making 2 quick contributions that sped up the uutils <code>dd</code> tool by 2-3x.</p> <table> <thead> <tr> <th></th> <th>bs=4k count=1000000</th> <th>bs=1M count=20000</th> <th>bs=1G count=10</th> </tr> </thead> <tbody> <tr> <td>GNU dd (baseline)</td> <td>1.15s</td> <td>1.07s</td> <td>1.13s</td> </tr> <tr> <td>Coreutils dd</td> <td>2.14s</td> <td>1.98s</td> <td>5.98s</td> </tr> <tr> <td>Coreutils dd (with <a href="https://github.com/uutils/coreutils/pull/3600" class="external-link" target="_blank" rel="noopener">#3600</a>)</td> <td>1.99s</td> <td>1.60s</td> <td>1.95s</td> </tr> <tr> <td>Coreutils dd (with <a href="https://github.com/uutils/coreutils/pull/3610" class="external-link" target="_blank" rel="noopener">#3610</a>)</td> <td>1.29s</td> <td>1.79s</td> <td>5.96s</td> </tr> <tr> <td>Coreutils dd (with both fixes)</td> <td>1.15s</td> <td>1.42s</td> <td>1.90s</td> </tr> </tbody> </table> <ul> <li>These results are for copying from /dev/zero to /dev/null to limit I/O impact and measure <code>dd</code> overhead as closely as possible. The commands used are something like: <code>time dd status=progress bs=1G count=10 &lt; /dev/zero &gt; /dev/null</code></li> </ul> <h2 id="understanding-dd"> Understanding &ldquo;dd&rdquo; <a class="heading-link" href="#understanding-dd"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I want to explain the two changes I made, but first we need a little background on the <code>dd</code> tool. <code>dd</code> is used to copy (and sometimes convert, which we&rsquo;ll be ignoring) data from one file to another. It can be setup to copy a specific number of bytes and can be configured to copy in blocks of a specified size.</p> <p>If you&rsquo;ve ever installed an .iso file to a USB drive on linux, you&rsquo;ve probably been directed to use <code>dd</code> with a command that looks something like this:</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>&gt; dd <span style="color:#c6a0f6">if</span><span style="color:#91d7e3;font-weight:bold">=</span>ubuntu-22.04.iso <span style="color:#f4dbd6">of</span><span style="color:#91d7e3;font-weight:bold">=</span>/dev/sdb <span style="color:#f4dbd6">bs</span><span style="color:#91d7e3;font-weight:bold">=</span><span style="color:#f5a97f">4096</span> </span></span></code></pre></div><p>That command sets the input file <code>if</code>, the output file <code>of</code>, and the blocksize <code>bs</code> (in bytes).</p> <p><code>dd</code> works by taking the following steps on its main loop:</p> <ol> <li>Read <code>blocksize</code> bytes from the input file into a buffer.</li> <li>Apply some conversions, if applicable.</li> <li>Write the buffer to the output file.</li> <li>Repeat from step 1 until finished (until it has written <code>count</code> blocks, it&rsquo;s hit the end of the file, or it errors out).</li> </ol> <p>Before I made any changes, here is the main loop for the <code>dd</code> utility in the uutils project: <a href="https://github.com/uutils/coreutils/blob/0acfa07d7707897e64ba3783cdc370f4e102388d/src/uu/dd/src/dd.rs" class="external-link" target="_blank" rel="noopener">source link</a></p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Start a thread that reports transfer progress. </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// When `status=progress` is given on the command-line, the </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// `dd` program reports its progress every second or so. We </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// perform this reporting in a new thread so as not to take </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// any CPU time away from the actual reading and writing of </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// data. We send a `ProgUpdate` from the transmitter `prog_tx` </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// to the receives `rx`, and the receiver prints the transfer </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// information. </span></span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> (prog_tx, rx) <span style="color:#91d7e3;font-weight:bold">=</span> mpsc::channel(); </span></span><span style="display:flex;"><span> thread::spawn(gen_prog_updater(rx, i.print_level)); </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// The main read/write loop. </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Each iteration reads blocks from the input and writes </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// blocks to this output. Read/write statistics are updated on </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// each iteration and cumulative statistics are reported to </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// the progress reporting thread. </span></span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">while</span> below_count_limit(<span style="color:#91d7e3;font-weight:bold">&amp;</span>i.count, <span style="color:#91d7e3;font-weight:bold">&amp;</span>rstat, <span style="color:#91d7e3;font-weight:bold">&amp;</span>wstat) { </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Read a block from the input then write the block to the output. </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// As an optimization, make an educated guess about the </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// best buffer size for reading based on the number of </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// blocks already read and the number of blocks remaining. </span></span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> loop_bsize <span style="color:#91d7e3;font-weight:bold">=</span> calc_loop_bsize(<span style="color:#91d7e3;font-weight:bold">&amp;</span>i.count, <span style="color:#91d7e3;font-weight:bold">&amp;</span>rstat, <span style="color:#91d7e3;font-weight:bold">&amp;</span>wstat, i.ibs, bsize); </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> (rstat_update, buf) <span style="color:#91d7e3;font-weight:bold">=</span> read_helper(<span style="color:#91d7e3;font-weight:bold">&amp;</span><span style="color:#c6a0f6">mut</span> i, loop_bsize)<span style="color:#91d7e3;font-weight:bold">?</span>; </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">if</span> rstat_update.is_empty() { </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">break</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> wstat_update <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span>.write_blocks(<span style="color:#91d7e3;font-weight:bold">&amp;</span>buf)<span style="color:#91d7e3;font-weight:bold">?</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Update the read/write stats and inform the progress thread. </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// If the receiver is disconnected, `send()` returns an </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// error. Since it is just reporting progress and is not </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// crucial to the operation of `dd`, let&#39;s just ignore the </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// error. </span></span></span><span style="display:flex;"><span> rstat <span style="color:#91d7e3;font-weight:bold">+=</span> rstat_update; </span></span><span style="display:flex;"><span> wstat <span style="color:#91d7e3;font-weight:bold">+=</span> wstat_update; </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> prog_update <span style="color:#91d7e3;font-weight:bold">=</span> ProgUpdate::new(rstat, wstat, start.elapsed()); </span></span><span style="display:flex;"><span> prog_tx.send(prog_update).unwrap_or(()); </span></span><span style="display:flex;"><span> } </span></span></code></pre></div><h2 id="performance-problem-1-buffer-reuse"> Performance Problem #1: Buffer Reuse <a class="heading-link" href="#performance-problem-1-buffer-reuse"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>The first problem becomes most obvious when <code>dd</code> is used with a large blocksize. I initially started looking at <code>dd</code> because of <a href="https://github.com/uutils/coreutils/issues/3094" class="external-link" target="_blank" rel="noopener">Issue #3094</a>, which hinted at the problem.</p> <p>The problem is that the main loop above doesn&rsquo;t reuse the same buffer for each copy. What the block above does is allocate a new buffer of <code>blocksize</code> bytes, then read from the input file into that buffer, write the output, then discard the buffer.</p> <p>What you want to do instead is to reuse the buffer to avoid reallocating memory. This is one of those problems that people who work in a higher level GC&rsquo;d language (like Javascript/Python) often miss while learning systems programming, so it&rsquo;s not too surprising to find something like this in a newbie Rust project. Rust tends to have a mix of both experienced systems programmers looking for an upgraded C++ (like me) and from web developers looking to write native code for the first time.</p> <p>Interestingly, this is actually a problem that Rust is pretty uniquely suited to solve safely. Safely reusing a buffer of memory is one of the problems that Rust&rsquo;s design is intended to help with. The <a href="https://github.com/uutils/coreutils/pull/3600" class="external-link" target="_blank" rel="noopener">actual code change</a> was relatively small, and Rust helped ensure that it was safe.</p> <p>This change alone sped up large <code>blocksize</code> copies by up to 3x.</p> <h2 id="performance-problem-2-thread-communication"> Performance Problem #2: Thread Communication <a class="heading-link" href="#performance-problem-2-thread-communication"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>The second problem here is a little less obvious, but shows up mainly when working with small blocksizes.</p> <p>Whoever wrote this code originally intended to optimize it by moving all of the output to a separate thread, as you can see from the comments. This resulted in sending a message to the output thread after every block was written in order to update the status bar.</p> <p><code>dd</code> only outputs the progress once every second to avoid spending too much time printing. The check to see if 1 second had passed was being done in the second thread, so the second thread would be woken up after every block was written but only have output to print once per second.</p> <p><strong>This is a perfect example of premature optimization</strong>, and, like the previous problem, also seems to come from someone unfamiliar with systems programming. Thread-to-thread communication is actually fairly expensive relative to other low-level operations, but you probably won&rsquo;t think of it that way if you&rsquo;re writing a higher-level language.</p> <p>In common usage, blocksizes are in the Kilobyte range, so often this loop will be iterated over thousands of times per second. I have real doubts that sending a message between threads like this is significantly faster than printing a line of text, but sending thousands of messages per second is definitely slower than printing once per second.</p> <p><a href="https://github.com/uutils/coreutils/pull/3610" class="external-link" target="_blank" rel="noopener">My fix</a> was ultimately a general fixing of problems related to output, which included a fix to only wake up the printing thread once per second. There were several other related bugs dealing with with the output that I also fixed, you can look at the PR for more info.</p> <p>Ultimately I didn&rsquo;t remove the second printing thread entirely because one signal per second wasn&rsquo;t ever going to have a significant performance impact, and the code is organized around having that extra thread. It&rsquo;s still there, but from a performance standpoint there&rsquo;s certainly no reason for it.</p> <p>For the smallest commonly used <code>blocksize</code> of 4K, this change alone sped up <code>dd</code> by almost 2x.</p> <h2 id="the-final-form"> The Final Form <a class="heading-link" href="#the-final-form"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>After my changes the core loop looks like so:</p> <div class="highlight"><pre tabindex="0" style="color:#cad3f5;background-color:#24273a;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Start a thread that reports transfer progress. </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// The `dd` program reports its progress after every block is written, </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// at most every 1 second, and only if `status=progress` is given on </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// the command-line or a SIGUSR1 signal is received. We </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// perform this reporting in a new thread so as not to take </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// any CPU time away from the actual reading and writing of </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// data. We send a `ProgUpdate` from the transmitter `prog_tx` </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// to the receives `rx`, and the receiver prints the transfer </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// information. </span></span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> (prog_tx, rx) <span style="color:#91d7e3;font-weight:bold">=</span> mpsc::channel(); </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> output_thread <span style="color:#91d7e3;font-weight:bold">=</span> thread::spawn(gen_prog_updater(rx, i.print_level)); </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> <span style="color:#c6a0f6">mut</span> progress_as_secs <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#f5a97f">0</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Create a common buffer with a capacity of the block size. </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// This is the max size needed. </span></span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> <span style="color:#c6a0f6">mut</span> buf <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#8aadf4">vec!</span>[<span style="color:#eed49f">BUF_INIT_BYTE</span>; bsize]; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// The main read/write loop. </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Each iteration reads blocks from the input and writes </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// blocks to this output. Read/write statistics are updated on </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// each iteration and cumulative statistics are reported to </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// the progress reporting thread. </span></span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">while</span> below_count_limit(<span style="color:#91d7e3;font-weight:bold">&amp;</span>i.count, <span style="color:#91d7e3;font-weight:bold">&amp;</span>rstat, <span style="color:#91d7e3;font-weight:bold">&amp;</span>wstat) { </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Read a block from the input then write the block to the output. </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// As an optimization, make an educated guess about the </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// best buffer size for reading based on the number of </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// blocks already read and the number of blocks remaining. </span></span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> loop_bsize <span style="color:#91d7e3;font-weight:bold">=</span> calc_loop_bsize(<span style="color:#91d7e3;font-weight:bold">&amp;</span>i.count, <span style="color:#91d7e3;font-weight:bold">&amp;</span>rstat, <span style="color:#91d7e3;font-weight:bold">&amp;</span>wstat, i.ibs, bsize); </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> rstat_update <span style="color:#91d7e3;font-weight:bold">=</span> read_helper(<span style="color:#91d7e3;font-weight:bold">&amp;</span><span style="color:#c6a0f6">mut</span> i, <span style="color:#91d7e3;font-weight:bold">&amp;</span><span style="color:#c6a0f6">mut</span> buf, loop_bsize)<span style="color:#91d7e3;font-weight:bold">?</span>; </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">if</span> rstat_update.is_empty() { </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">break</span>; </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> wstat_update <span style="color:#91d7e3;font-weight:bold">=</span> <span style="color:#91d7e3">self</span>.write_blocks(<span style="color:#91d7e3;font-weight:bold">&amp;</span>buf)<span style="color:#91d7e3;font-weight:bold">?</span>; </span></span><span style="display:flex;"><span> </span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// Update the read/write stats and inform the progress thread once per second. </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// If the receiver is disconnected, `send()` returns an </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// error. Since it is just reporting progress and is not </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// crucial to the operation of `dd`, let&#39;s just ignore the </span></span></span><span style="display:flex;"><span> <span style="color:#6e738d;font-style:italic">// error. </span></span></span><span style="display:flex;"><span> rstat <span style="color:#91d7e3;font-weight:bold">+=</span> rstat_update; </span></span><span style="display:flex;"><span> wstat <span style="color:#91d7e3;font-weight:bold">+=</span> wstat_update; </span></span><span style="display:flex;"><span> <span style="color:#ed8796">let</span> prog_update <span style="color:#91d7e3;font-weight:bold">=</span> ProgUpdate::new(rstat, wstat, start.elapsed(), <span style="color:#f5a97f">false</span>); </span></span><span style="display:flex;"><span> <span style="color:#c6a0f6">if</span> prog_update.duration.as_secs() <span style="color:#91d7e3;font-weight:bold">&gt;=</span> progress_as_secs { </span></span><span style="display:flex;"><span> progress_as_secs <span style="color:#91d7e3;font-weight:bold">=</span> prog_update.duration.as_secs() <span style="color:#91d7e3;font-weight:bold">+</span> <span style="color:#f5a97f">1</span>; </span></span><span style="display:flex;"><span> prog_tx.send(prog_update).unwrap_or(()); </span></span><span style="display:flex;"><span> } </span></span><span style="display:flex;"><span> } </span></span></code></pre></div><h2 id="conclusions"> Conclusions <a class="heading-link" href="#conclusions"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>I enjoyed these little code fixes, they gave me a chance to practice some of my Rust skills and maybe help out the community if this project ever becomes more popular. I&rsquo;ve occasionally done similar fixes, or looked into other performance problems in different projects, so I may write up more about them in the future.</p> <p>For anyone wanting to do something similar checking out the PRs I&rsquo;ve linked here will point you to several more areas for optimization or fixes in just this one tool.</p> Stimulus Bundling for Good and Evil https://jackson.dev/post/2016-07-21-stimulus-bundling-good-evil/ Thu, 21 Jul 2016 00:00:00 +0000 https://jackson.dev/post/2016-07-21-stimulus-bundling-good-evil/ <p>If you enjoy something, you&rsquo;ll tend to do it more often.</p> <p>Obviously.</p> <p>However, there tend to be a lot of things that you know are good for you, but that aren&rsquo;t exactly enjoyable. Today we&rsquo;re going to try to fix that through a technique I call Stimulus Bundling; the goal of which is to make those healthy but boring things more enjoyable.</p> <p>The example I&rsquo;ll be using today is exercise, but it can also apply to any number of other things: cooking rather than eating out, working on your side project or hobby, reading instead of watching TV, etc.</p> <p>The basic idea is simple: <strong>bundle together something you genuinely enjoy with something you want to enjoy.</strong></p> <p>We can also use this technique for the opposite case as well, for trying to eliminate things you know are bad for you but that are enjoyable. I&rsquo;ll discuss that at the end.</p> <h1 id="people-vs-dogs"> People vs Dogs <a class="heading-link" href="#people-vs-dogs"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>All we&rsquo;re really doing here is using <a href="https://en.wikipedia.org/wiki/Classical_conditioning" class="external-link" target="_blank" rel="noopener">Classical Conditioning</a>, AKA Pavlovian Conditioning, to modify your response to stimuli. You have probably heard about it from the example of Pavlov&rsquo;s dogs.</p> <p>Ivan Pavlov ran an experiment in which he conditioned dogs to salivate in response to a totally unrelated stimulus (a buzzer, as it&rsquo;s usually related, though I couldn&rsquo;t find a direct confirmation). He did this by setting the buzzer off every time the dogs were fed so that later the dogs began to anticipate food whenever they heard the buzzer. The dogs learned to associate the buzzer with food directly.</p> <p>Humans are smarter than dogs [citation needed] but the same general principle applies to us as well. Bundle together the things that are good but boring with the things you find enjoyable. Eventually, you will be trained to enjoy them both.</p> <h1 id="positive-training-in-practice"> Positive Training In Practice <a class="heading-link" href="#positive-training-in-practice"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>At this point, you&rsquo;re probably thinking I&rsquo;m an idiot, because obviously everybody knows that. Lots of people set up rewards for themselves whenever they stick to their goals. Whenever they workout they reward themselves with a smoothie, or ice cream, or a day off of working out.</p> <p>Well, that&rsquo;s not the same thing. That methodology (of rewards and punishments) is Operant Conditioning and while it can be used to influence behavior it doesn&rsquo;t necessarily encourage you to enjoy the act.</p> <p>The goal should not be to mollify yourself after you&rsquo;ve done something you don&rsquo;t like, it should be to train yourself to start enjoying it.</p> <p>Classical Conditioning can help us do that. And once we start enjoying it we&rsquo;ll start doing it more without having to exert any unnecessary <a href="https://jackson.dev/post/2016-01-19-willpower-is-a-limited-resource/" >Willpower</a>.</p> <p>Classical Conditioning works best when the two stimuli are done either concurrently or very close together (i.e. on the order of seconds), so rewarding yourself after the fact isn&rsquo;t going to help. The goal then is to find something you can do concurrently with the difficult activity.</p> <p>What you pair with your difficult activity is up to you, but you should pick something that is enjoyable and is not directly bad for you.</p> <p>I&rsquo;ve always found that the best things to pair with exercise, or other relatively monotonous work, are things you can listen to or watch while your exercise. For example, you could listen to or watch:</p> <ul> <li>An audiobook</li> <li>Your favorite TV show</li> <li>An informational podcast</li> <li>Or music</li> </ul> <p>After a few sessions, you&rsquo;ll start to associate working out with the enjoyment you get from those entertainments. And every time you see references to your entertainment you&rsquo;ll start to crave a workout.</p> <p>This effect sticks around for quite a long time. For a month or two back in college, I listened to one particular album on repeat while working out on an exercise bike a few times a week. It was probably 10-20 workouts total.</p> <p>It&rsquo;s been half a decade but I still feel like working out whenever I hear those songs.</p> <p>The real key to building this association is the more difficult part: whatever you pick, do it as little as possible outside of your paired activity.</p> <p>Keep them exclusive as much as possible. If you&rsquo;re watching your favorite TV show(s) while you&rsquo;re on the treadmill, don&rsquo;t watch them while you&rsquo;re not. You&rsquo;ll get the added incentive to go workout just so you get to see what happens next, and you&rsquo;ll cement the association faster.</p> <h1 id="negative-training-in-practice"> Negative Training In Practice <a class="heading-link" href="#negative-training-in-practice"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>As much as you can use this technique for making healthy activities enjoyable, you can also use this for making unhealthy enjoyable things even <em>more</em> addictive.</p> <p>Using the same principle above, what do you think happens when you spend a few evenings a week watching your favorite TV show and eating dessert?</p> <p>Those two things become <em>very</em> enjoyable for you. A few days a week turns into every single day, and it becomes difficult to only watch TV and not eat dessert with it.</p> <p>This is what fast food companies catch you with using a combination of sugar, salt, and fat. Bars take it a step further by adding alcohol into the mix too.</p> <p>This is what movie theaters do when they teach you that movies must be enjoyed with popcorn and soda. If you have them both together often enough you&rsquo;ll always need popcorn at the movies or it will feel like something&rsquo;s missing.</p> <p>And it&rsquo;s also what you do to yourself whenever you take your unhealthy things in groups.</p> <p>Just be aware of what you&rsquo;re doing to yourself, of how you are conditioning your own expectations.</p> <h1 id="rules-to-live-by"> Rules to Live By <a class="heading-link" href="#rules-to-live-by"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>What you need to learn from Stimulus Bundling can be broken down into 3 simple rules.</p> <ul> <li>Pair a healthy, boring stimuli with something else that is enjoyable and non-detrimental.</li> <li>Never do one without the other.</li> <li>Don&rsquo;t pair two unhealthy, enjoyable stimuli together.</li> </ul> You Need a Password Manager https://jackson.dev/post/2016-07-13-password-manager/ Wed, 13 Jul 2016 00:00:00 +0000 https://jackson.dev/post/2016-07-13-password-manager/ <p>Today I&rsquo;m going to talk about something that may seem a little off-topic; you need to have better passwords for your online accounts.</p> <p>This is probably not news to you. Every time a big website gets hacked there is a flood of news stories about the dangers of weak passwords, but it always dies down quickly. You might feel guilty about using &lsquo;123456&rsquo; as your password for a couple days, but you&rsquo;ll eventually forget about it once the media stops reminding you.</p> <p>Here&rsquo;s the bottom line; <em>if you are not using a different, strong password for every online account, you are putting yourself at unnecessary risk.</em></p> <p>Luckily there&rsquo;s a simple way to do that. All while actually making it easier for you to remember your passwords. Just use a password manager.</p> <p>But before I get to talking about that, I&rsquo;m going to try and explain why you actually need one. Where the risks actually come from.</p> <h1 id="whats-the-deal-with-passwords"> What&rsquo;s the deal with Passwords <a class="heading-link" href="#whats-the-deal-with-passwords"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>The risks that I&rsquo;m talking about are not what you first think of when you hear the phrase &lsquo;steal your password&rsquo;. I&rsquo;m not talking about somebody who stands behind you at the coffee shop and watches you type it in. Nor the risk of somebody compromising your computer with a virus. Nor the risk of somebody in a coffee shop snooping on the wifi and grabbing your passwords from any unsecured traffic.</p> <p>Password managers will actually help with those, but most people already understand how to prevent them. Don&rsquo;t type an easy password in with a stranger over your shoulder, install some antivirus on your computer, and make sure the websites you log into use HTTPS. (Ok, maybe the last one isn&rsquo;t <em>that</em> obvious.)</p> <p>But what we&rsquo;re concerned about today is the big breaches. It&rsquo;s not about some individual trying to steal your login, it&rsquo;s about some individual trying to steal millions of logins.</p> <p>Let me explain.</p> <p>When you create an account on a website, let&rsquo;s say Example.com, they need to store your login information.</p> <h2 id="idea-1"> Idea #1 <a class="heading-link" href="#idea-1"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>At Example.com they&rsquo;re pretty naive. So they take your username and your password and store them together in a database. When you log in they compare your username and password against the database and if they match, they let you in. If you ever forget your password, they can just email it to you.</p> <p>This works perfectly until somebody hacks in and steals the database, and they can read all the passwords.</p> <h2 id="idea-2"> Idea #2 <a class="heading-link" href="#idea-2"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>In response, Example.com upgrades their security. Now instead of storing them in plain text they encrypt all of the passwords. Whenever you log in they just decrypt your password stored in the database and use that to check if you got it right. As a bonus, if you ever forget your password they are still able to just email it to you.</p> <p>This works perfectly until somebody steals their database and their encryption key, which they had to store on the same computer, and decrypts all the passwords.</p> <p>(No website today should <em>ever</em> do either of those. If any website is ever able to email you your password, <strong>run away and don&rsquo;t look back</strong>. Then never use that password again.)</p> <h2 id="idea-3"> Idea #3 <a class="heading-link" href="#idea-3"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Now they get creative. There is a type of computer algorithm that is one-way &rsquo;encryption&rsquo;, called hashing algorithms. These algorithms take an input, such as a password, and produce an output &lsquo;hash&rsquo; that is unique to that input. So every time they run your password through that algorithm it returns the same semi-random output.</p> <p>What makes hashing algorithms interesting is that there&rsquo;s no way to get from the output hash to the input password.</p> <p>Now Example.com will save in their database your username (in plain text) and your hashed password. When you log in, they take your password, run it through their hashing algorithm, and compare the result against the saved hash. If you forget your password, they won&rsquo;t be able to send it to you because even they don&rsquo;t know what it is.</p> <p>Example.com is now up to par with many websites out there today. Often when there is a &lsquo;password breach&rsquo; it&rsquo;s somebody stealing a database containing plain text usernames and the password hashes, just like I&rsquo;ve described. The passwords leaked in the last couple months from LinkedIn were secured like this.</p> <p>This is what we&rsquo;re trying to secure against by using a password manager.</p> <h2 id="why-its-still-broken"> Why It&rsquo;s Still Broken <a class="heading-link" href="#why-its-still-broken"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>The problem with this scheme is not immediately obvious.</p> <p>If you had an account with Example.com and your password was &lsquo;123456&rsquo;, and they used the hashing algorithm MD5, your password hash would be: E10ADC3949BA59ABBE56E057F20F883E</p> <p>In fact, everybody who used &lsquo;123456&rsquo; would have the same password hash. All an attacker has to do is check to see whose password hash is E10ADC3949BA59ABBE56E057F20F883E and they&rsquo;ll know that your password is &lsquo;123456&rsquo;.</p> <blockquote> <p>Amusingly, if you store password hints alongside the password hashes into the database, a leaked password database becomes <a href="https://xkcd.com/1286/" class="external-link" target="_blank" rel="noopener">The Greatest Crossword Puzzle in the History of the World</a>. Line up the password hashes and you get multiple hints to explain a single password.</p> </blockquote> <p>To find the passwords, an attacker will take every password they can think of, generate the hash value, and check to see if the hash value is in the database. The more passwords they check the longer it takes them, so they use lists of the most common passwords, dictionary words, <a href="https://xkcd.com/936/" class="external-link" target="_blank" rel="noopener">basic changes to dictionary words</a>, and ultimately random guessing.</p> <p>Every password they check takes time, as they have to compute the hash then check it against the database. A good hash algorithm is actually somewhat slow, so it takes longer to check each password, but not too slow that it takes a long time for you to log in.</p> <p>What you need to do is make sure your password is not one of the passwords that they can check in a reasonable amount of time. Because their ability to &lsquo;guess&rsquo; what the passwords may be is very very good, you should assume that anything you can remember easily is not secure. Thus, you need to use a complex password that you can&rsquo;t easily remember.</p> <p>If you reuse passwords for multiple websites, you are trusting <em>all of them</em> to keep that password secure for you. If any one of them doesn&rsquo;t store their passwords correctly, and you reuse your username, then none of those accounts are secure. Once a hacker finds your password at one site, they will try the same username/password combination at other sites. So it&rsquo;s good to have different passwords for <em>everything</em>.</p> <p>If you&rsquo;re still hoping that the websites you use probably have good security practices, remember that LinkedIn, <em>a multi-billion dollar company</em>, didn&rsquo;t do this correctly.</p> <p>So the best thing you can do to keep your accounts secure is to have a unique, difficult to remember password for every account. The only easy way to do that is with a password manager.</p> <blockquote> <p>There is actually a better way for a website to store passwords, called password salting. Basically, they generate a random &lsquo;salt&rsquo; they store in the database (in plain text) along with your username and password. When you log in, they take your password, add the salt to it, then they hash that whole thing to compare to your stored password hash. Since the &lsquo;salt&rsquo; is unique to your account, any attacker would have to try to crack your password individually, instead of being able to crack all of the passwords at the same time.</p> </blockquote> <h1 id="so-a-password-manager"> So a Password Manager <a class="heading-link" href="#so-a-password-manager"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Using a password manager helps you solve the two basic problems most people have with their passwords: uniqueness and complexity.</p> <p>Since you don&rsquo;t have to remember your passwords you can have a different password for every single account. That way if one of them is breached only one of your accounts will be compromised. If you have hundreds of different accounts like I do, there is no way you can keep track of them all without a password manager.</p> <p>Since you don&rsquo;t have to remember your passwords, they can all be very complex. &ldquo;ap49nQXj*ro@&rdquo; is a strong password, but would take most people quite a bit of effort to memorize. My password manager will generate it and save it for me in seconds.</p> <p>With a password manager, you will only have to memorize one strong password, your &lsquo;master password&rsquo;, and all the rest will be stored for you.</p> <p>There are a number of options to choose from, but for the most part all of them will have the same basic set of features.</p> <ol> <li>A very secure &lsquo;vault&rsquo; for storing all your passwords.</li> <li>A &lsquo;password generator&rsquo; to create new passwords. Humans are notoriously bad at generating random passwords.</li> <li>Simple ways to access your passwords, like a browser extension.</li> </ol> <p>Personally, I use <a href="https://lastpass.com/" class="external-link" target="_blank" rel="noopener">LastPass</a>, but I&rsquo;ve also heard very good things about the usability of <a href="https://1password.com/" class="external-link" target="_blank" rel="noopener">1Password</a>. Between the two of them is really a matter of preferences.</p> <p>The only downside to those two options is that you have to trust those companies to some extent. If you are extra paranoid then the best option is <a href="http://keepass.info/" class="external-link" target="_blank" rel="noopener">KeePass</a>, but it might be more effort than it&rsquo;s worth.</p> <p>Look into the options for yourself, pick the one you&rsquo;re comfortable with, and then <strong>actually use it.</strong></p> I Logged All My Eating for a Month https://jackson.dev/post/2016-06-21-logged-eating-month/ Tue, 21 Jun 2016 00:00:00 +0000 https://jackson.dev/post/2016-06-21-logged-eating-month/ <p>Recently I&rsquo;ve been trying to lose a bit of weight.</p> <p>Not a lot, just 10-15 pounds. I&rsquo;m relatively fit but I&rsquo;ve packed on a little bit of unwanted fat over the years.</p> <p>For a few months I did the easy thing. I said to myself &lsquo;Hey, we&rsquo;re gonna lose weight now,&rsquo; then proceeded to do mostly nothing.</p> <p>I was arrogant enough to believe that because I wanted to lose weight, and because I knew how to eat healthy, that I would actually drop those pounds without effort. That I could just go about my day and make smart food choices that would result in me eating at a low enough deficit to drop weight.</p> <p>In short, I was an idiot. I actually mentioned how much of a bad idea that is <em><a href="https://jackson.dev/post/2016-01-12-resolutions" >back on this blog, while I was doing it myself.</a></em> Obviously it didn&rsquo;t work.</p> <p>So a month ago I decided to try an experiment: I would actually listen to what I already knew was the best advice for losing weight. I would actually track all of my food intake.</p> <p>There have been a rather large number of studies showing that tracking your eating in a food diary helps people lose weight. You don&rsquo;t even have to track calories if you don&rsquo;t want to, the mere act of keeping track of what you&rsquo;re eating forces you to make healthier food choices. The assumption is that when you have to keep track of your food you will make better choices about what you&rsquo;re eating. <a href="https://www.sciencedaily.com/releases/2008/07/080708080738.htm" class="external-link" target="_blank" rel="noopener">A 2008 study, for example, [showed that keeping a food diary will <em>double</em> weight loss.]</a></p> <p>Even while I was starting I still assumed that I wouldn&rsquo;t have to change my eating habits much. I assumed that I&rsquo;d start tracking my food and just confirm my already perfect eating habits.</p> <p>I very quickly learned that that was not the case at all.</p> <h1 id="the-plan"> The Plan <a class="heading-link" href="#the-plan"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Every day I did two things: tracked everything I ate, and logged my weight and body fat percentage every day.</p> <p>I need to clarify two things in there:</p> <p>First, the reason I logged my weight every day is because I assumed my weight would not change very much, if at all, so having a marker every day would help me see if there was a trend. You really should not do this if you aren&rsquo;t prepared to see your weight fluctuate by up to several pounds a day. (It&rsquo;s due to hydration and food intake, not actually gaining or losing weight.) You&rsquo;ll freak out if it&rsquo;s high for a few days and be tempted to celebrate with more food if it looks like you &rsquo;lost&rsquo; several pounds since the day before.</p> <p>Second, I measured body fat percent with <a href="https://en.wikipedia.org/wiki/Bioelectrical_impedance_analysis" class="external-link" target="_blank" rel="noopener">one of these electronic scales</a>. The method that these use to measure body-fat is not very accurate but it&rsquo;s possible they could help show a trend. This will hopefully help illustrate that I&rsquo;m losing a greater percentage of fat than I am of lean body mass.</p> <h1 id="the-results"> The Results <a class="heading-link" href="#the-results"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p><img src="https://jackson.dev/2016/06/weight-graph.png" alt="Graph of my weight generated on MyFitnessPal"></p> <p>On day one I weighed 186.6 pounds at a body fat percentage of 17.4%.</p> <p>On day 30 I weighed 180.2 pounds at a body fat percentage of 16.4%.</p> <p>Today (day ~35) I weigh 179 pounds at a body fat percentage of 16.0%.</p> <p>Even taking into account regular weight fluctuations, at a minimum I lost 3-4 pounds. Keep in mind this is after several months of &rsquo;trying&rsquo; without losing anything, and somewhere between 3 and 7 pounds in a month is a decent amount of weight to lose for someone of my size.</p> <p>More valuable to me than just the weight coming off was what I learned from doing this.</p> <h1 id="heres-what-i-learned"> Here&rsquo;s What I Learned <a class="heading-link" href="#heres-what-i-learned"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <h3 id="i-was-grossly-over--or-under-estimating-the-calories-in-my-food"> I was grossly over- or under-estimating the calories in my food <a class="heading-link" href="#i-was-grossly-over--or-under-estimating-the-calories-in-my-food"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Some meals or snacks that I thought weren&rsquo;t <em>too</em> bad, like cheese and salami, turned out to be quite a bit higher in calories than I thought. I mean, I knew there was a decent amount of calories in cheese but I didn&rsquo;t realize there were 120 calories per <em>ounce</em>.</p> <p>In the opposite direction, the side of veggies that I usually have with dinner had even fewer calories than I thought. Riced cauliflower, which I&rsquo;ve grown partial to as a &lsquo;rice&rsquo; replacement, has only ~120 calories per <em>pound</em>. Coincidentally, that&rsquo;s the same number of calories that are in the tablespoon of oil or butter that I cook it in.</p> <p>You may have seen <a href="http://www.wisegeek.com/what-does-200-calories-look-like.htm" class="external-link" target="_blank" rel="noopener">these comparisons online before</a>, pictures of what the same number of calories look like in different foods, but it doesn&rsquo;t really sink in until you pay attention to them for quite a bit longer than the time it takes to read about it.</p> <h3 id="it-was-only-difficult-at-first"> It was only difficult at first <a class="heading-link" href="#it-was-only-difficult-at-first"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>At the beginning of the month it was somewhat difficult. At the end of every day I was still feeling hungry; I really just wanted to sit down and eat a huge dessert.</p> <p>But no matter how big the dessert was I&rsquo;d still be hungry. And interestingly, even going to bed hungry I wouldn&rsquo;t <em>wake up</em> hungry at all. I usually wouldn&rsquo;t feel hungry again until noon.</p> <p>Clearly my body just wanted me to eat more for no real reason. I got through it either by snacking on veggies or by having a small dessert and sucking it up.</p> <p>After 2 weeks or so, that feeling mostly went away. Either I got used to it or I adjusted to the calories, either way it wasn&rsquo;t nearly as difficult.</p> <h3 id="use-a-tool"> Use a tool <a class="heading-link" href="#use-a-tool"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>This is a lot easier if you use something like <a href="http://www.myfitnesspal.com/" class="external-link" target="_blank" rel="noopener">MyFitnessPal</a>. Their food diary is built for this, and you can fairly easily just look up the nutrition info for something you&rsquo;re eating inside of the tool.</p> <p>Setting up an account on MyFitnessPal will help you figure out what your caloric needs for each day are. Just go through their short questionnaire, tell it if you&rsquo;re trying to lose, gain, or maintain your weight, and it&rsquo;ll estimate how many calories a day you should be eating.</p> <p>This is monumentally easier and more useful than writing it down in a notebook. I bet it&rsquo;d take 10x longer if you wanted to track how many calories you were eating by hand.</p> <h3 id="if-youre-going-to-log-it-dont-wait"> If you&rsquo;re going to log it, Don&rsquo;t Wait <a class="heading-link" href="#if-youre-going-to-log-it-dont-wait"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>The longer you wait after you eat the less likely it is you&rsquo;ll actually write it down. Truthfully I sometimes took to even logging my food before I ate it. That way I knew what I was getting into.</p> <p>The several days in the past month where I didn&rsquo;t log what I ate were caused by this. I got distracted and didn&rsquo;t take the time to log it while I was eating, so I slipped up.</p> <p>It happens, just keep going starting with your next meal.</p> <h3 id="if-youre-not-exact-overestimate"> If you&rsquo;re not exact, overestimate <a class="heading-link" href="#if-youre-not-exact-overestimate"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>There will come a time when you don&rsquo;t know exactly how much stuff you just ate. Was that one cup of rice or one and a half? How much butter did I use for my eggs?</p> <p>The best way to log that is to be perfect. Weigh all your food using your kitchen scale and log it down to the gram.</p> <p>The second best way is to do it occasionally, and use that to estimate all the other times. Use the scale to calibrate your food estimating superpower.</p> <p>But since you aren&rsquo;t going to be perfect no matter which way you go, overestimate. Round up your estimates a little bit to give yourself some extra leeway.</p> <p>[Note that if you are trying to gain weight, obviously you should do the opposite. Underestimate a little bit in your estimates.]</p> <h3 id="dont-panic"> Don&rsquo;t Panic <a class="heading-link" href="#dont-panic"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>There is not a lot of harm in missing your eating goals for the day, so long as you aren&rsquo;t doing it every day.</p> <p>Some days I went over my calorie goal by a couple hundred, but most days I was comfortably under.</p> <h1 id="what-im-doing-now"> What I&rsquo;m Doing Now <a class="heading-link" href="#what-im-doing-now"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>At the end of my 30 days I decided that I&rsquo;d do a different trial. For the next 30 days I&rsquo;m going back to not keeping a food diary.</p> <p>I&rsquo;m curious to see if I&rsquo;ll continue losing weight without actually tracking all of my food. My theory is that I will lose some, but maybe not as much as I did during the trial. I am hoping that the things I learned about my eating and the habits I started to form over the past month will help me stay on track.</p> <p>What made me hopeful that that is actually true and not wishful thinking was a recent trip to Vegas.</p> <p>Last weekend, just before my food diary trial ended, my girlfriend and I went to Vegas. Since we understand math enough to not bother gambling, we went to enjoy the shows and the food, so to better enjoy the short vacation I decided not to log any of my food for those days. I can enjoy a steak more if I ignore the fact that it&rsquo;s 1000 calories.</p> <p>That decision was a <em>little</em> risky. I&rsquo;m prone to binge eating so it could have gone horribly with the fancy Vegas buffets, but it didn&rsquo;t. Compared to my past habits, I felt much more satisfied with less food. I ate as much as I wanted, but it wasn&rsquo;t more food than I needed.</p> <p>The day after we came back was the end of my 30 days, and I hit the lowest weight of the trial.</p> <p>So now I&rsquo;m going to see if I can keep up the weight loss without tracking everything. The only thing I&rsquo;ll track is my weight, so I can have some marker for progress.</p> <p>If I lose about the same amount of weight again, putting me down to somewhere in the 174-176 range, I&rsquo;ll call it a success, otherwise I&rsquo;ll go back to tracking everything.</p> <p>I&rsquo;ll touch back here next month to update how it went.</p> The Weekly Review https://jackson.dev/post/2016-05-24-the-weekly-review/ Tue, 24 May 2016 00:00:00 +0000 https://jackson.dev/post/2016-05-24-the-weekly-review/ <p>Feeling like you&rsquo;re working hard but not getting anything done? As if you don&rsquo;t know what you actually accomplished in the past month or 3?</p> <p>The simple truth is: you&rsquo;re disorganized.</p> <p>I&rsquo;m not talking about how clean your home is, you are disorganized in how you manage your time. Your productivity system is in shambles.</p> <p>Sure, maybe you have all 327 tasks you need to do saved to your favorite task manager that you can access from your smartwatch at all times of the day or night, but that doesn&rsquo;t mean you&rsquo;re getting any of them done. And it definitely doesn&rsquo;t mean you&rsquo;re getting the <em>right tasks</em> done at the <em>right time</em>.</p> <p>If you&rsquo;re spinning your wheels, feeling disorganized or not &lsquo;on top&rsquo; of things, you&rsquo;re almost certainly neglecting an essential part of any productivity system: The Weekly Review.</p> <h1 id="basics-of-a-productivity-system"> Basics of a Productivity System <a class="heading-link" href="#basics-of-a-productivity-system"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Your personal productivity system probably doesn&rsquo;t align perfectly with anything spouted by the &rsquo;experts&rsquo;. Though to be honest, there are so many different supposed &lsquo;correct&rsquo; systems that you&rsquo;d never be able to figure that out anyways.</p> <p>Or maybe you don&rsquo;t have any system at all, in which case you should probably fix that.</p> <p>Personally, I tend to work with a mix of GTD (<a href="https://en.wikipedia.org/wiki/Getting_Things_Done" class="external-link" target="_blank" rel="noopener">Getting Things Done</a>) and <a href="https://en.wikipedia.org/wiki/Deep_Work" class="external-link" target="_blank" rel="noopener">Deep Work</a>. Most of what I&rsquo;m talking about today is taken from the Weekly Review ideas in GTD.</p> <p>Regardless of what system you are using, any good, complete system is going to have a few things in common.</p> <ol> <li>Track tasks somewhere outside your own head.</li> <li>Schedule work according to its importance/deadline.</li> <li>Do the work.</li> <li>Review the results and course correct.</li> </ol> <p>And that&rsquo;s really it. You can add all sorts of other things on top of that; Inbox Zero, Pomodoro Sprints, etc. You can organize everything using a notebook or in the cloud. It doesn&rsquo;t matter.</p> <p>What matters is that it works for you.</p> <p>The problem is that a lot of people forget about that last bit, the Review. Some days it can feel pointless, and not worth your time. After all, you know what you&rsquo;ve already completed.</p> <p>But it is important. It&rsquo;s an opportunity for you to review what you&rsquo;ve been doing, ensure you&rsquo;re on track, and look to the future to see where you want to go.</p> <p>Think of it as a weekly check in with yourself. It&rsquo;s a time to touch base with all of the things in your life, both personal and professional, that you care about. It&rsquo;s a time to think about not just <em>doing</em> work, but to take a step back and review the <em>how</em> and the <em>why</em> of the work you&rsquo;re doing.</p> <p>Most importantly, it&rsquo;s a time to view all of your life together in one place and plan the direction you want to go. A high-level planning session if you will.</p> <p>So let&rsquo;s get started.</p> <h1 id="schedule-your-review"> Schedule Your Review <a class="heading-link" href="#schedule-your-review"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Start by scheduling the review in your calendar. The first time may take you an hour or two if you&rsquo;re being thorough, but after doing one or two it will probably take less time (especially if you&rsquo;re on top of things).</p> <p>You can do one as often as you&rsquo;d like, but once a week tends to work best. Schedule it either for the end of your workweek or the beginning of your workweek.</p> <p>Personally, I block off time at the end of my workweek to go through it. Doing it just before the weekend helps to clear my head of work and unload that from my mind. Knowing that I&rsquo;m up to date on everything helps keep me from thinking about work too much so I can enjoy the weekend.</p> <p>When I get back to work on Monday mornings I like to plan out my work for the week as much as possible. Reading through the Weekly Review from the week before re-loads all of that information into my mind, and helps me make smart decisions on where I should be directing my efforts for the week.</p> <p>You could, of course, do both your planning and review at the same time. I prefer to split them up because it gives me some solid bookends for my weeks worth of work. I start the week by scheduling work by importance into time blocks, and I end the week by taking a higher level look at my progress and performance, which then feeds into how I prioritize and schedule work for the following week.</p> <h1 id="do-the-weekly-review"> Do the Weekly Review <a class="heading-link" href="#do-the-weekly-review"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>When it comes time to do the review, don&rsquo;t try to remember everything you need to do. Use a checklist.</p> <p>The GTD community has created lots of complicated templates to provide direction on doing a review, but personally I find simpler is better. Here is the very basic Weekly Review Checklist that I use [<a href="http://gettingthingsdone.com/wp-content/uploads/2014/10/Weekly_Review_Checklist.pdf" class="external-link" target="_blank" rel="noopener">PDF Warning</a>]. I customized mine a little bit, but overall it is still very similar. I&rsquo;d suggest you do the same; start with this simple checklist and, once you start to see what works for you, make changes to it to suit your own needs.</p> <p>Let me walk through and explain the different steps. If you&rsquo;re not familiar with GTD then some of their explanations probably won&rsquo;t make sense.</p> <p>The Weekly Review has three different parts: Get Clear, Get Current, and Get Creative.</p> <h2 id="get-clear"> Get Clear <a class="heading-link" href="#get-clear"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>We start by getting all your various inboxes cleared out. If you&rsquo;ve stayed on top of this throughout the week it won&rsquo;t take long.</p> <ul> <li><strong>Collect Loose Papers and Materials</strong> <ul> <li>Gather together everything you need to process. Receipts, notes, etc. If you don&rsquo;t work with paper do this digitally.</li> </ul> </li> <li><strong>Get “IN” to Zero</strong> <ul> <li>Inbox Zero. Everything you need to &lsquo;process&rsquo; you should. Make sure everything in your Inboxes (physical and digital) are sorted out and processed. You shouldn&rsquo;t have any unknown things you need to do after this is done.</li> </ul> </li> <li><strong>Empty Your Head</strong> <ul> <li>Put down into your task list/calendar/notebook anything you haven&rsquo;t yet written down. Don&rsquo;t rely on your memory to keep track of things you need to do.</li> <li>Many people find that a &lsquo;Trigger List&rsquo; of potential tasks is useful to remind them of things to do. <a href="http://wiki.43folders.com/index.php/Trigger_List" class="external-link" target="_blank" rel="noopener">Something like this.</a></li> </ul> </li> </ul> <h2 id="get-current"> Get Current <a class="heading-link" href="#get-current"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Next, we review <em>everything</em> to get up to date. Check in with everything you&rsquo;ve got going on and everything you need to do, don&rsquo;t leave anything out.</p> <ul> <li><strong>Review Action Lists</strong> <ul> <li>Read through your task list. Mark off things you&rsquo;ve completed and ensure it&rsquo;s organized how you like.</li> <li>If I find a couple small (&lt;2 minute) tasks that I&rsquo;ve forgotten about I tend to just do them immediately.</li> </ul> </li> <li><strong>Review Previous Calendar Data</strong> <ul> <li>Check in with what you did this past week for any remaining tasks or follow-up.</li> <li>What I also like to do is review how successfully I managed my time over the past week. While going over the past calendar I ask myself: <ul> <li>What was my most useful time spent?</li> <li>What was my least useful time spent?</li> </ul> </li> </ul> </li> <li><strong>Review Upcoming Calendar</strong> <ul> <li>Review what you&rsquo;re doing next week (or even farther out). Schedule anything you need to do to prepare, or resolve any conflicts you find.</li> </ul> </li> <li><strong>Review Waiting For List</strong> <ul> <li>Check your lists of things that you are waiting for. E.g. waiting for an email response? Ping them or schedule a follow-up.</li> </ul> </li> <li><strong>Review Project (and Larger Outcome) Lists</strong> <ul> <li>Evaluate status of projects, goals, and outcomes, one by one, ensuring at least one current action item on each. Browse through project plans, support material, and any other work-in-progress material to trigger new actions, completions, waiting for’s, etc.</li> </ul> </li> <li><strong>Review Any Relevant Checklists</strong> <ul> <li>Use as a trigger for any new actions.</li> </ul> </li> </ul> <h2 id="get-creative"> Get Creative <a class="heading-link" href="#get-creative"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Lastly, we look towards the future. This is a time to plan for things you want to do (personally or professionally) and try to fit them into your calendar.</p> <ul> <li><strong>Review Someday Maybe List</strong> <ul> <li>For GTD, this is a combined Bucket List / Future Projects list. While going over these look for next steps you can fit into your schedule. <ul> <li><a href="https://jackson.dev/post/2016-05-12-future-self-building-better-bucket-list/" >The Future Self</a> is my personal &lsquo;Someday Maybe&rsquo; list, and I review it at this point.</li> </ul> </li> <li>You may find it helpful to also have a work related &lsquo;Someday Maybe&rsquo; list. Keep a list of professional development things you want to do, future work-related projects, etc.</li> </ul> </li> <li><strong>Be Creative and Courageous</strong> <ul> <li>Any new, wonderful, hare-brained, creative, thought-provoking, risk-taking ideas to add into your system??? Add it to your Future Self list and start planning for it.</li> </ul> </li> </ul> <h1 id="thats-it"> That&rsquo;s It <a class="heading-link" href="#thats-it"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>That&rsquo;s all there is to it. At the end of your Review you should be up to date on everything you&rsquo;re doing in your personal and professional life. You shouldn&rsquo;t have any outstanding work to do that isn&rsquo;t scheduled (or at least saved and written down). You should be aware of all your future plans, and you should be comfortable with how your next few weeks are going to go.</p> <p>Most importantly, you should know whether or not you&rsquo;re on track to what you want to be doing. And if you&rsquo;re not, this is the time for you to identify that and schedule time to get back on track.</p> <p>Remember, this is something you should enjoy. It&rsquo;s a time for you to take stock and sort through your life. To figure out what you&rsquo;re doing and where you&rsquo;re going.</p> <p>So indulge yourself a little. Go to your favorite coffee shop, or work on it over a glass of wine. Celebrate a little, it&rsquo;s the end of a long  (and hopefully productive) week.</p> <p>Just don&rsquo;t forget to work on it, it will probably turn out to be the most valuable hour of work you do all week.</p> Why You Will Fail to Act in an Emergency https://jackson.dev/post/2016-05-17-will-fail-act-emergency/ Tue, 17 May 2016 00:00:00 +0000 https://jackson.dev/post/2016-05-17-will-fail-act-emergency/ <p>On a cold winter night in 1964, Catherine &lsquo;Kitty&rsquo; Genovese was attacked outside her apartment in Queens, New York. Coming back from work at 3 a.m., she was stabbed in the back but managed to scare away her attacker. Several minutes later her attacker returned, raped her, and ultimately killed her.</p> <p>The New York Times reported that 37 or 38 people witnessed the murder, but not a single one of them called the police. This is a murder that took place over the course of <em>half an hour</em>, in front of dozens of witnesses, with no calls to the police.</p> <p>At the time, this was baffling to behavioral scientists. No one could explain how that many people could witness such a thing and not help her.</p> <p>No one except the killer. When asked why he would risk doing that in front of so many witnesses, he replied <a href="https://www.psychologytoday.com/blog/not-just-bystander/201403/the-1964-kitty-genovese-tragedy-what-have-we-learned" class="external-link" target="_blank" rel="noopener">&lsquo;I knew they wouldn&rsquo;t do anything, people never do&rsquo;</a>.  Obviously he didn&rsquo;t know why, but it turns out he was mostly right.</p> <p>The widespread publicity around this story sparked research into what became known as the Bystander Effect.</p> <p><em>A quick side note: While researching this article I was surprised to learn that the story as I&rsquo;ve related it above (and as it&rsquo;s depicted in most descriptions) is not entirely accurate. While that is what was reported by the NYT, and what sparked the research, later investigations showed a very different picture.</em></p> <ul> <li><em>There were probably closer to 12 witnesses, none of whom saw the whole attack. Most couldn&rsquo;t see the attack at all, they only heard what sounded like a domestic dispute.</em></li> <li><em>Only 1 witness saw that she was stabbed in the initial attack, and I couldn&rsquo;t verify if</em> that <em>person ever called the police.</em></li> <li><em>The police were called after the initial attack, but were only told a woman was &lsquo;beat up, but got up and was staggering around.&rsquo; They didn&rsquo;t respond.</em></li> <li><em>After the second, fatal attack, the police were called and arrived within minutes.</em></li> <li><em>One witness was already there comforting Kitty even though she couldn&rsquo;t have known that the attacker had left.</em></li> </ul> <p><em>While this case is not</em> exactly <em>accurate, it</em> is <em>the story used in pretty much every social psychology textbook, as it&rsquo;s a very simple (and extreme) description of the Bystander Effect.</em></p> <h1 id="the-bystander-effect"> The Bystander Effect <a class="heading-link" href="#the-bystander-effect"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Let&rsquo;s say you witness a tragedy: a car accident, a violent domestic dispute, or even something more mundane, like a child scraping their knee. If you&rsquo;re the only witness, you&rsquo;re probably going to do something.</p> <p>But if there are lots of witnesses, you&rsquo;re less likely to get involved.</p> <p>And that makes sense. If 10 people watch something happen, it usually just takes one person to help, so 90% of the time you don&rsquo;t have to do anything at all. Right?</p> <p>That certainly <em>sounds</em> true, and in some ways it is. But when everyone feels the same way, that someone else will probably help, then <em>nobody</em> will act. So counterintuitively, if you fall and hit your head it might be safer to do it in front of 1 person than in front of 10, and not just because of the embarrassment factor.</p> <p>When there are more people present, it&rsquo;s easy to look around and think &lsquo;oh, no one else is doing anything, so I don&rsquo;t have to either.&rsquo; And that&rsquo;s the problem of the Bystander Effect.</p> <p>There are a few reasons to explain why this happens, the big ones being:</p> <ul> <li><strong>Diffusion of responsibility.</strong> You&rsquo;re less likely to take responsibility for action or inaction when others are present.</li> <li><strong>Group cohesion</strong>. Members of a group are more likely to stick within the group than break out of it. If nobody else is doing anything, you won&rsquo;t want to break the mold.</li> </ul> <p>It&rsquo;s an interesting effect, and there are all sorts of neat social psych experiments showing how we react to it. But there&rsquo;s no need to go read social psychology papers to see it in action (you probably don&rsquo;t find those as interesting as I do).</p> <p><em>What Would You Do?</em> is a prime time TV show on ABC that has been running for 7 seasons so far. The entire premise of the show is to set up some scenario in public and see if the bystanders respond, all recorded on hidden cameras of course. They mostly try to test various public prejudices (e.g. have a black male steal a bike in a public park and see if anyone responds, then do the same thing with a white female and compare the responses) but at its core it is an examination of the Bystander Effect.</p> <p>(If you&rsquo;re curious about the above scenario, the black male was reported immediately every time they tried it, but some of the bystanders actually helped the attractive white woman steal the bike, even after she told them she was stealing it.)</p> <p>If you find it hard to believe that you wouldn&rsquo;t respond in an emergency, watch that show sometime, I guarantee it will change your mind.</p> <h1 id="so-what-would-you-do"> So What Would You Do? <a class="heading-link" href="#so-what-would-you-do"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>A few months ago I was vacationing with my family in Savannah, GA. Savannah has a relatively small and walkable historic district so we spent a lot of time walking around the city.</p> <p>At one point as we were walking together we witnessed a car accident. At least one person was injured, but it didn&rsquo;t look life threatening. Not counting the people involved there were maybe 5 or 6 people there as it happened.</p> <p>My mother called 911 immediately, but I&rsquo;m not sure if anyone else did, including the 6 or 7 other people who walked over to see what was going on. A couple other people were on the phone but they may have been on the phone beforehand.</p> <p>Now my mom did the right thing. She saw that no one next to her had called 911 yet, so she did herself. Good job Mom!</p> <p>Because of where everybody was standing on the street, several of the witnesses had no way of knowing if anyone else was getting help but still did nothing except watch. They assumed somebody else was doing it. In fact, I&rsquo;ll admit that that was my first instinct too, even though I really should have known better.</p> <p>The truth is it takes a lot of training to make sure you&rsquo;re going to act correctly in an emergency. EMTs, nurses, and flight attendants are known for their cool-headedness in emergencies because they&rsquo;ve been trained for it. You probably haven&rsquo;t, so you&rsquo;re probably going to react slowly in a real emergency.</p> <p>In most cases, the hardest thing to overcome will be your natural inclination to stick with the crowd.</p> <p>I won&rsquo;t pretend to be an expert at dealing with emergency situations, but here&rsquo;s my 2 cents worth of advice on dealing with the Bystander Effect in an emergency.</p> <h2 id="if-you-are-the-one-that-needs-help"> If you are the one that needs help <a class="heading-link" href="#if-you-are-the-one-that-needs-help"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Perhaps the most useless thing to do if you need help and are surrounded by people is to yell &lsquo;someone call 911/help me/etc&rsquo;. It puts the responsibility of taking that action on everyone there, playing right into the hands of the Bystander Effect. So don&rsquo;t do that.</p> <p>Instead, single out 1 person from the crowd and give them specific instructions. More specific instructions are more likely to elicit a response. &lsquo;Come here&rsquo; or &lsquo;call 911&rsquo; is much better than &lsquo;help me&rsquo;.</p> <p>By singling out 1 person from the crowd, the diffusion of responsibility that cripples the crowd is gone. <em>That one person is now responsible for providing help</em>, and it could make all the difference for you.</p> <h3 id="if-you-are-witness-to-some-emergency"> If you are witness to some emergency <a class="heading-link" href="#if-you-are-witness-to-some-emergency"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>The first researchers to demonstrate the Bystander Effect (John Darley and Bibb Latane) noted that bystanders go through five stages of reacting to an emergency.</p> <ol> <li><em>Notice</em> that something is going on</li> <li><em>Interpret</em> the situation as being an emergency</li> <li>Determine their <em>degree of responsibility</em></li> <li>Determine the <em>form of assistance</em></li> <li><em>Implement</em> the action choice</li> </ol> <p>Step 3, the degree of responsibility felt by each person, is the tricky one; that&rsquo;s where the Bystander Effect will get you. If other people are there you will have a smaller degree of responsibility.</p> <p>But you know this now. You know that <a href="https://jackson.dev/post/2016-01-26-cognitive-biases/" >your brain is kind of stupid, and doesn&rsquo;t always do the right thing.</a></p> <p>And knowledge is power. If you ever catch yourself going through these steps in an emergency, just ignore everything that tells you that <em>you</em> don&rsquo;t have much responsibility. Skip calculating step 3 entirely, always just assume that on a scale of 1 – 10 your &lsquo;degree of responsibility&rsquo; is stuck on 11.</p> <p>If you recognize it&rsquo;s an emergency ask yourself &lsquo;<strong>What do I need to do?</strong>&rsquo; instead of &lsquo;Do I need to do anything?&rsquo;.</p> <p>Obviously the real difficulty is actually recognizing you&rsquo;re in that situation, but unfortunately a blog post can only help so much with that.</p> <p>Just try to remember to be bold, and unafraid to go against the crowd. One day you may just save a life.</p> The Future Self: Building a Better Bucket List https://jackson.dev/post/2016-05-12-future-self-building-better-bucket-list/ Thu, 12 May 2016 00:00:00 +0000 https://jackson.dev/post/2016-05-12-future-self-building-better-bucket-list/ <p>I have a problem with traditional bucket lists.</p> <p>Most people make a &lsquo;bucket list&rsquo; at some point in their life. It&rsquo;s simple in concept; you sit down and make a list of all of the wonderful things you want to experience before you die. There&rsquo;s certainly nothing wrong with that, it&rsquo;s great to have a list to remind you of all of the things you dream of doing.</p> <p>The problem is that the &lsquo;bucket list&rsquo; that most people end up making is woefully incomplete.</p> <p>I build my version of a &lsquo;bucket list&rsquo; not just as end goals, but as a living document for the direction I want myself to go. An all-encompassing description of what I want to do and what I want to be like.</p> <p>Unlike the usual &lsquo;bucket list&rsquo;, I intentionally don&rsquo;t plan on finishing everything on it. In fact, many of the things on my list <em>can&rsquo;t</em> be crossed off. Not everything in life can be whittled down to a checkmark.</p> <p>Also crazy to me is this idea that people only make a list of things they want to do. A list of goals and travel locations and lifestyle milestones does not define a person, and is a very small part of what you actually want for yourself. Having visited the 7 wonders of the world doesn&rsquo;t fundamentally change your everyday experience.</p> <p>So my version of a &lsquo;bucket list&rsquo; is a description of a new version of my self. <a href="https://jackson.dev/post/2016-04-19-the-ideal-self" >Last week I talked about the distinction between your Real Self and your Ideal Self</a>, and this vision document fits in nicely with those concepts: I call it the Future Self.</p> <h1 id="the-future-self"> The Future Self <a class="heading-link" href="#the-future-self"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>My Future Self document is a description of where I want to go in the future. It&rsquo;s not a list of end goals, though that is a part of it, but instead a vision of the type of lifestyle I want to have, the experiences and abilities I want to have, and the type of person I&rsquo;d like to be.</p> <p>To be fair, maybe other people have always seen a &lsquo;bucket list&rsquo; this way; I&rsquo;m certainly not claiming that this is an original thought. Rather, I think that most people make the mistake of seeing a &lsquo;bucket list&rsquo; one-dimensionally, as literally just a list of things you want to do before you kick the bucket, and hopefully I can convince some of those people to broaden their horizons a bit.</p> <p>Ok, I&rsquo;ve been going on and on about this for a bit too long now, what exactly makes it different from a &lsquo;bucket list&rsquo;?</p> <h2 id="slightly-different-contents"> (Slightly) Different Contents <a class="heading-link" href="#slightly-different-contents"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Your Future Self is obviously going to be very personal, and it&rsquo;s going to take some time for you to figure out exactly what you think it&rsquo;s best to include. I tend to think about them in terms of 4 broad categories:</p> <ul> <li>Goals and milestones to achieve.</li> <li>Knowledge or skills to master.</li> <li>Habits or activities to do regularly.</li> <li>Character traits to embody.</li> </ul> <p>Maybe you don&rsquo;t find all of those types of things useful to write down. Perhaps you don&rsquo;t care what type of person you are so long as you reach all of your other goals in life.</p> <p>That&rsquo;s ok, if you want to be a narcissistic billionaire go right ahead. But personally I&rsquo;d rather be a better person than have a slightly longer number in my bank account, and writing that down in a place I&rsquo;ll revisit keeps me mindful of that fact.</p> <p>There&rsquo;s one thing you should be wary of when deciding what to include. For many people, a &lsquo;bucket list&rsquo; turns into &rsquo;the 50 places to see before I die&rsquo;. I hate that for two reasons: first, it will never live up to your expectations (see <a href="https://en.wikipedia.org/wiki/Paris_syndrome" class="external-link" target="_blank" rel="noopener">Paris Syndrome</a>), and second, actually going to all those places is enormously inefficient. In a world where climate change is real and caused by humans, flying halfway around the world to spend a day or two looking at pretty old things is at best a frivolous luxury, and at worst culpable malfeasance. If you&rsquo;re going to travel, do it efficiently</p> <h2 id="prioritization-and-planning"> Prioritization and Planning <a class="heading-link" href="#prioritization-and-planning"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>If your &lsquo;bucket list&rsquo; has 100 things on it, how do you possibly get anything done?</p> <p>Most people realize that if they have some type of project they&rsquo;re planning for, things need to be done in a certain order, and some tasks are more important than others. To get anything done you have to organize it in a way that makes sense, sorting both by urgency and by importance.</p> <p>Treat your Future Self just the same as you would any other project. Plan, prioritize and prepare accordingly.</p> <p>Pick a few things you&rsquo;re going to do soon, and lay out your strategy to get there. Add the steps you need to take into your task system and schedule them. Make progress <em>now</em> on the things you can do and keep the rest of it around for the future.</p> <p>If you&rsquo;re so inclined, set deadlines for certain things, or even schedule them in the far off future. Want to celebrate your 25th wedding anniversary in Paris? Put it in your calendar even if it&rsquo;s decades away.</p> <p>Use it to start planning your life intentionally. Don&rsquo;t treat your life goals the way you&rsquo;d treat a disorganized project; figure out what you really want and then make sure it happens.</p> <h1 id="how-do-you-use-it"> How Do You Use It? <a class="heading-link" href="#how-do-you-use-it"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>How to use a &lsquo;bucket list&rsquo; may seem like an odd topic, but bear with me.</p> <p>Making a &lsquo;bucket list&rsquo; and sticking it in a drawer never to be seen again is a waste of your precious time. If you&rsquo;re going to take the time out of your day to sit down and dream of what you want to do and <em>be</em>, you should take care to actually move in that direction.</p> <p>Like I said earlier, the Future Self is a <em>living document</em>, one that you need to look at and update regularly for it to be of any use. Personally I like to review it at least every month, though usually I&rsquo;ll look it over at least briefly as part of my <a href="https://jackson.dev/post/2016-05-24-the-weekly-review/" >Weekly Review</a>.</p> <p>When you review it, don&rsquo;t just read it and dream of a far-off future. Sure that&rsquo;s part of it, but make sure you also look at it with an eye towards updating it. Remove things that no longer matter to you and add things that do.</p> <p>Review it to look for things you can work towards. Do you have some extra time in the evenings to fill? Pick something off your Future Self list and get to work.</p> <p>Most importantly, review it with an eye towards reminding yourself who you want to be. Take note of where you are now, and if you aren&rsquo;t moving in the direction you want to make sure you change it.</p> <h1 id="get-started"> Get Started <a class="heading-link" href="#get-started"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Schedule some time <em>now</em> to get started. When you first set out to writing things down it may take a little bit of time. Set aside a half hour or an hour, and write down a list of things you want to do, things you want to try, and traits you want to have. Reviewing it will take much less time.</p> <p>Make sure it&rsquo;s in a place you can access easily. A notebook you carry with you, a saved entry in your favorite note-taking software, or a simple text file you keep on your desktop. You can use whatever works best for you.</p> <p>If you need some ideas to get started, here&rsquo;s the current version of mine. Feel free to borrow anything you&rsquo;d like, but these types of things are highly personal so I expect yours will look vastly different very quickly.</p> The Ideal Self https://jackson.dev/post/2016-04-19-the-ideal-self/ Tue, 19 Apr 2016 00:00:00 +0000 https://jackson.dev/post/2016-04-19-the-ideal-self/ <p>Self-actualization sits at the peak of Maslow&rsquo;s Hierarchy of Needs, and while you may not have heard the word it&rsquo;s what you&rsquo;ve been searching for out of life. To get there we need to internalize the differences between our ideal self and our real self, which is what we&rsquo;re going to examine today.</p> <p>I think Maslow put it best by defining it simply as &ldquo;the full realization of one&rsquo;s potential&rdquo;, and of one&rsquo;s &rsquo;true self&rsquo;. In this context, &lsquo;reaching your full potential&rsquo; doesn&rsquo;t refer to reaching the end of your quest; finishing school, or getting that big promotion. What matters is that you are fulfilling your potential and your true self, <em>today</em>.</p> <p>You don&rsquo;t have to have ticked off everything on your bucket list, or even be at a fantastic place in your life. You just need to fully understand yourself and be satisfied with where you are in life.</p> <p>Self-actualizers feel that they live a fulfilling life in a way that is true to themselves. And while their lifestyles may differ they do usually <a href="https://en.wikipedia.org/wiki/Self-actualization#Maslow.27s_characteristics_of_self-actualizers" class="external-link" target="_blank" rel="noopener">have some commonalities.</a></p> <ul> <li>Realistic perceptions of themselves, others and the world around them.</li> <li>Acceptance of oneself, others, and nature with all of their flaws.</li> <li>Humanitarian. They tend to be driven by some mission bigger than themselves.</li> <li>Independent. Reliant on their own experiences and judgment, not culture and environment.</li> <li>Autonomy. They tend to be independent, self-reliant, and comfortable with working alone.</li> <li>Profound interpersonal relationships. Deep, loving bonds with a few intimate friends rather than many superficial relationships.</li> <li>Seek out <a href="https://en.wikipedia.org/wiki/Peak_experience" class="external-link" target="_blank" rel="noopener">Peak Experiences</a>. Moments with intense joy, wonder, and deep meaning.</li> </ul> <p>Personally, I think &lsquo;self-actualization&rsquo; sums up my goals of personal growth quite nicely. And knowing the goal, I&rsquo;ve been working on using the techniques from humanistic psychology to progress along the path to self-actualization.</p> <p>The most useful concept that I&rsquo;ve found is an understanding of the differences between my &lsquo;ideal self&rsquo; and my &lsquo;real self&rsquo;.</p> <h1 id="the-ideal-self-vs-the-real-self"> The Ideal Self vs The Real Self <a class="heading-link" href="#the-ideal-self-vs-the-real-self"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>When you think about yourself, you typically envision two different versions of &lsquo;self&rsquo;.</p> <ol> <li><strong>The real self</strong>. How you act and function in everyday life.</li> <li><strong>The ideal self</strong>. Your idea of what you wish you were as a person. What you believe you should be doing.</li> </ol> <p>For most people, the ideal self and the real self <em>do not match</em>, and it is that disparity that generates internal conflict. Going about your life experiencing one thing but believing that you should be doing another is, to put it bluntly, not good. It&rsquo;s the root cause of personal dissatisfaction and may even cause depression.</p> <p>For the self-actualizer, these two visions of themselves match up. There&rsquo;s nothing that they wish they should be doing that they aren&rsquo;t actually doing.</p> <p>To merge these two realities, we need to remain mindful of ourselves each day and understand what we want (or feel we should) be doing.</p> <h1 id="become-mindful-of-the-real-self"> Become Mindful of the Real Self <a class="heading-link" href="#become-mindful-of-the-real-self"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Being aware of how you actually act and being present in your life sounds like something that everyone obviously does, but it&rsquo;s not so simple.</p> <p>If someone tells me they want to lose weight and asks for advice, the best &rsquo;tip&rsquo; I can give them is to track everything they eat. Just knowing what you are eating, and how many calories you are consuming, encourages healthy choices. You&rsquo;ll eat healthier without really trying.</p> <p>The same should be true for the rest of your life. Understanding what you&rsquo;re doing should help you make better choices.</p> <p>The best way to do that, to stay present and &rsquo;live in the moment&rsquo;, is to have some form of meditation practice in your life. For the uninitiated, mindfulness meditation is simply training the mind to be present as often as possible. To be aware of yourself and what you are doing throughout the day and avoiding the temptation to get lost in thought.</p> <p>The truth is, you probably don&rsquo;t realize how often you are lost in thought until you try meditating.</p> <p>I mentioned meditation before during my <a href="https://jackson.dev/post/2016-02-23-the-benefits-of-cold-showers" >Cold Shower experiment</a>. I practice it (semi-)regularly using <a href="https://www.headspace.com/" class="external-link" target="_blank" rel="noopener">Headspace</a>, which I highly recommend. I&rsquo;ve also heard good things about <a href="https://www.calm.com/" class="external-link" target="_blank" rel="noopener">Calm.com</a> but haven&rsquo;t tried it myself.</p> <p>This won&rsquo;t be the last time I recommend it, so give it a try today. It really will help you slow down and come to a better understanding of your own mind.</p> <p>After you figure out what you are, you can then worry about figuring out what you want.</p> <h1 id="understand-your-ideal-self"> Understand Your Ideal self <a class="heading-link" href="#understand-your-ideal-self"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Too often when people think of their ideal self they don&rsquo;t think of what they actually want for themselves <em>today</em>. Your ideal self is what you can be in the present, not what you want to be in the future.</p> <p>Whether you&rsquo;ve written it down or not, you probably have a &lsquo;bucket list&rsquo; of things you want to do in your life. All the places you want to go, things you want to do, etc. And that&rsquo;s great, it helps you define where you want to go in the future, but it doesn&rsquo;t help you define your ideal self.</p> <p><em>I&rsquo;m going to talk more about the future self and bucket lists in my next article, but first w__e need to come to an understanding of what your ideal self actually is.</em></p> <p>To come to know your ideal self, I have a short exercise for you to do. We&rsquo;re going to visualize a day in the life of your ideal self. Feel free to write it down somewhere if you&rsquo;d like, if you&rsquo;re so inclined you can even review it each week in your <a href="https://jackson.dev/post/2016-05-24-the-weekly-review/" >Weekly Review</a>, but spending a few minutes visualizing is ok too.</p> <p>So picture this:</p> <p><strong>It&rsquo;s a random Tuesday a few weeks/months/years from now, and you have a normal day of work ahead of you. What does your day look like?</strong></p> <ul> <li>When/where do you wake up?</li> <li>Are you alone?</li> <li>What does your morning routine look like?</li> <li>What do you eat?</li> <li>What do you have to do today?</li> <li>Who are you doing it with?</li> <li>What are your evening plans?</li> <li>Are you going to exercise/cook/read/game?</li> </ul> <p>That description you&rsquo;re writing down, that image you&rsquo;re connecting with, <em>is your ideal self</em>. It&rsquo;s not some fictionalized, grandiose image of yourself in the future, it&rsquo;s how you want to be <em>right now</em>.</p> <p>That being said, any answer is ok so long as it&rsquo;s what you truly want. Personally, my answers are pretty mundane.</p> <p>Waking up early next to someone I love. Spending most of the day in solitude focused on challenging, fulfilling Deep Work. Taking time for myself to exercise my body and stretch my mind. Creating tasty, healthy food for myself and any close friends that happen to be around. Working on that personal side-project in the evening.</p> <p>Everyone has their own flavor of course, but I&rsquo;d bet that most people aren&rsquo;t all that different. Maybe you want the hectic lifestyle of a corporate ladder climber or to be a jet-setting titan of industry, but it&rsquo;s more likely you only think you do. Being busy isn&rsquo;t a badge of honor.</p> <p>Once you have this detailed understanding of your ideal self, you can start to note the (often small) deviations you take throughout the day that throw you off the path. And by remaining present and mindful of yourself throughout the day you can make adjustments to stay on track with how you want to live your life.</p> <p>Next week I&rsquo;ll talk about the future. For now, connect with yourself, meditate, and move just a little bit closer to self-actualization.</p> <hr> <p><em>Image By FireflySixtySeven [<a href="http://creativecommons.org/licenses/by-sa/4.0" class="external-link" target="_blank" rel="noopener">CC BY-SA 4.0</a>], via Wikimedia Commons</em></p> Education Reform https://jackson.dev/post/2016-04-05-education-reform/ Tue, 05 Apr 2016 00:00:00 +0000 https://jackson.dev/post/2016-04-05-education-reform/ <p>Ensuring that every child has access to a quality education is one of the most important things we can do as a society. Unfortunately we seem to be pretty bad at it.</p> <p>And I&rsquo;m not just talking about the U.S. (which ranks about average among developed nations, while spending much more per student).</p> <p>Every school system is stuck in the past. It&rsquo;s not <em>entirely</em> the fault of those working in it, but it does need to be fixed. It requires a massive overhaul of the system, changing the way we teach students and giving more power to individual teachers to help the kids that need it most.</p> <p>I have an (unoriginal) idea for a way to revamp the education system, make sure the best teachers are well-compensated and for all kids to get the same access to education, regardless of their parent&rsquo;s income.</p> <p>It&rsquo;s simple, the teachers should just not teach.</p> <p>That&rsquo;s it. Case closed. Problem solved.</p> <h1 id="uhh-what"> Uhh, What? <a class="heading-link" href="#uhh-what"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>I get it. You&rsquo;re probably asking yourself why that would help. Teachers are supposed to teach their students.</p> <p>But the problem is that there&rsquo;s no guarantee that <em>your</em> teacher is any good, we haven&rsquo;t found a great way to figure that out. Observing teachers doesn&rsquo;t work, because you can&rsquo;t observe every teacher every day.</p> <p>Grading teachers based on the performance of their students is a good idea in theory, but terrible in practice. It leads to teachers viewing poorly performing students as dangerous to their jobs, which is the <em>exact opposite</em> incentive we want to give them. We don&rsquo;t want teachers resenting poor students; those students should be the ones getting the most help.</p> <p>All you can do is <em>hope</em> that the school your kid goes to has great teachers. Though if you&rsquo;re willing to buy a more expensive house you can increase your odds; school funding in the U.S. is largely supported by property taxes, so if you live in an expensive area then your schools will be better funded.</p> <p>But there&rsquo;s no reason to settle for that. <em>I</em> can teach Algebra just as well as the best Algebra teachers in the country. Most other subjects too, and I wouldn&rsquo;t even have to teach.</p> <p>See, I&rsquo;m really just not very good at lecturing. Lesson plans, explaining complex things to tiny humans, it&rsquo;s all very difficult. So I don&rsquo;t think I&rsquo;d bother.</p> <p>I&rsquo;d just get somebody else to do it.</p> <p>All I&rsquo;d have to do is find video lectures of somebody else teaching the subject. After all, a video of the best Algebra teacher in the country isn&rsquo;t that much different than the real thing.</p> <p>Of course I would certainly need to <em>know</em> the subject. If any student had trouble I could work with them one on one to help them catch up. But since I wouldn&rsquo;t need to waste time lecturing, I&rsquo;d be able to work with the students that need help all day long.</p> <h1 id="optimization"> Optimization <a class="heading-link" href="#optimization"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>One thing you should know about me, I hate unnecessary work. There&rsquo;s no reason to spend 2 hours working on something when you can get the same result in half the time.</p> <p>So I don&rsquo;t understand why every year, in thousands of classrooms across the country (millions around the world), a teacher plans out and gives a lesson to their students on why 2 + 2 = 4. Or explains to their students why Pluto isn&rsquo;t a planet.</p> <p>That information isn&rsquo;t changing (usually). It isn&rsquo;t different from school to school.</p> <p>So why the hell do we make teachers waste their time teaching it?</p> <p>If instead of giving a lecture the teacher could direct the students to watch a video, it would free up hours of their time each week. So not only is it better for the students, it&rsquo;s better for the teachers.</p> <h1 id="higher-quality-teaching"> Higher Quality Teaching <a class="heading-link" href="#higher-quality-teaching"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Perhaps the most difficult part of being a quality teacher is knowing how to teach a group of kids with the same lesson.</p> <p>Different people learn best in different ways. Some people are visual learners, some audio, some tactile, etc. Each kid may have a different style that they work best with: some of the kids in class may learn best if they just read the material, some if they hear it explained succinctly, some may need to be hands on with everything.</p> <p>The real value in a great lecturer lies in their ability to cater lessons to all of their students, and all of their diverse learning styles. That is really hard. And for some strange reason, we make every teacher do this every time they teach a course, even though it&rsquo;s the same lesson.</p> <p>That&rsquo;s just stupid.</p> <p>If instead we have a managed set of video lectures, it is relatively easy to make sure they are high quality. We can have a centralized group of highly skilled people whose only job is to create the best lectures possible.</p> <p>Naturally we&rsquo;d give students access to these lectures at any time, so if they have trouble with a lesson they can always just watch it again.</p> <p>No longer would you have to worry about if your kid went to a good school or not. Every student would have access to the same education.</p> <p>And even though the lectures would be standardized, the freedom this gives to teachers would allow them to become much better at teaching the outliers in their subjects. Advanced students can work ahead and students that are behind could easily get help. Without the need to worry about lectures, a great teacher can become a great tutor to all of their students.</p> <h1 id="in-practice"> In Practice <a class="heading-link" href="#in-practice"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Interestingly, this level of catering to the students has already been tried on a large scale: homeschooling. In the U.S. today, there are over 2 million children being homeschooled.</p> <p>If public schools started using taped lectures they would look a lot less like they do now and far more like homeschooling. The lesson plans could be better catered to the individual student, and they&rsquo;d get more one on one time with the teacher.</p> <p>And homeschooling, if you didn&rsquo;t know, is <em>far</em> more effective than public schools. Which seems obvious, a homeschooled kid can spend more time on the subject they struggle with and less on the ones that come easily. A public school can&rsquo;t do that yet.</p> <p>Homeschooled children perform better than their peers on standardized tests, <a href="http://www.hslda.org/research/ray2003/Fig1.gif" class="external-link" target="_blank" rel="noopener">fare better in college</a>, and despite most assumptions they have <a href="http://www.hslda.org/docs/nche/000000/00000068.asp" class="external-link" target="_blank" rel="noopener"><em>better</em> social skills than their public school peers</a>. Most tellingly, a study of homeschooled students showed that <a href="http://www.hslda.org/docs/nche/000010/200410250.asp" class="external-link" target="_blank" rel="noopener">the performance gap between the children of high and low income families virtually disappears</a>, and the racial gap is reduced significantly.</p> <p>[<a href="http://www.collegeathome.com/homeschool-domination/" class="external-link" target="_blank" rel="noopener">I didn&rsquo;t include it here due to it&rsquo;s size, but here is a great infographic comparing homeschooling to public schools.</a>]</p> <p>Those studies are also 10-20 years old, when homeschooling was more difficult than it is today and public schools were largely the same.</p> <p>The truth is, today I could homeschool a kid and give them a far better education than our broken education system. There is absolutely zero reason to send them to a public school if I want them to reach their full academic potential.</p> <p>But we can change that. All it takes is a bit of investment and a willingness to challenge the status quo.</p> <h1 id="today"> Today <a class="heading-link" href="#today"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>In some ways, the education system is already going in that direction. There have been a few limited cases where schools have experimented with &lsquo;reverse schooling&rsquo; or &lsquo;reverse instruction&rsquo;, where the teacher assigns video lessons for homework and helps the students work through problems during class. But it astonishes me that it&rsquo;s used so rarely even though the resources are already available.</p> <p>Online sites like <a href="https://www.coursera.org/" class="external-link" target="_blank" rel="noopener">Coursera</a> or <a href="https://www.udacity.com/" class="external-link" target="_blank" rel="noopener">Udacity</a> already have full courses that you can take (most of them free too). <a href="http://ocw.mit.edu/index.htm" class="external-link" target="_blank" rel="noopener">MIT put most of their courses online and available for free years ago</a>. But those are all for college level coursework.</p> <p><a href="https://www.khanacademy.org/" class="external-link" target="_blank" rel="noopener">Khan Academy</a> is the largest resource I know of for this type of schooling at the K-12 level. For the past several years, Khan Academy has been building the resources to teach a variety of subjects using taped lectures.</p> <p>Math is the easiest thing to teach in this manner, so that&rsquo;s what they did first. They&rsquo;ve built out lessons and practice problems to teach someone everything from basic arithmetic to college level calculus with no gaps. Practice problems are generated programmatically so you can do as many as you need, and you can check progress every step of the way. If a teacher wants to use it to teach a classroom of kids, Khan Academy has all the resources necessary to teach them and monitor their progress.</p> <p>A math teacher can point their students to Khan Academy and they don&rsquo;t have to worry about lesson plans again. They can focus all their time on helping the students who need it, and Khan Academy will even identify those students for them.</p> <p>They also have lessons on science, economics, the arts, and computing. If you&rsquo;re looking for more, another good resource is <a href="https://www.youtube.com/user/crashcourse/playlists" class="external-link" target="_blank" rel="noopener">Crash Course on Youtube</a>, which has been making lessons on everything from World History to Philosophy to Astronomy.</p> <p>There are lessons available for free on almost every subject. All you have to do is search.</p> <h1 id="the-future"> The Future <a class="heading-link" href="#the-future"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Of course there are going to be issues with getting there. Individual teachers can&rsquo;t take the initiative and do it themselves, since in the current system it would probably be seen as them not doing their jobs. Much of this change needs to come from the top down, and that only happens when we start to re-think the status quo and focus on effectiveness over tradition.</p> <p>Look, <em>today</em> I could teach a kid far more effectively than public schools do. I could tailor their learning experience so they could advance just as fast or as slow as they needed to.</p> <p>With the tools we have <em>today</em> the public school system could do the same, it just takes a mindset shift and a concerted effort from some smart people.</p> <p>The public school system is a horrible failure in many ways, and if you can manage the logistics of it then homeschooling your child will already provide them with a much better education. The statistics <em>prove it.</em></p> <p>They can learn economics <a href="https://www.khanacademy.org/economics-finance-domain" class="external-link" target="_blank" rel="noopener">from a hedge-fund manager turned teacher (the founder of Khan Academy)</a>, calculus <a href="http://ocw.mit.edu/index.htm" class="external-link" target="_blank" rel="noopener">from an MIT professor</a>, and astronomy <a href="https://www.youtube.com/playlist?list=PL8dPuuaLjXtPAJr1ysd5yGIyiSFuh0mIL" class="external-link" target="_blank" rel="noopener">from an actual astronomer</a>.</p> <p>And that&rsquo;s all available for free, <em>today</em>.</p> <p>If the public school system doesn&rsquo;t adapt quickly, the disparity <em>will</em> become even greater; I won&rsquo;t be able to put my kids into the public school system in good conscience.</p> <p>Given the choice between an underpaid, overworked high school teacher and the most eloquent educators in the world, I&rsquo;ll choose the latter every time. All it&rsquo;ll cost me is the price of a cheap laptop and an internet connection, both of which I already have in my shitty apartment.</p> <p>It baffles me as to why the U.S. spends more than $5,000 a year <em>per student</em> to do worse than that.</p> <hr> <p><em>Photo by Malate269 (Own work) [Attribution], <a href="https://commons.wikimedia.org/wiki/File%3AAndrew_Classroom_De_La_Salle_University.jpeg" class="external-link" target="_blank" rel="noopener">via Wikimedia Commons</a></em></p> Fake People Are Better Than You (or WWBD?) https://jackson.dev/post/2016-03-29-fake-people-better-wwbd/ Tue, 29 Mar 2016 00:00:00 +0000 https://jackson.dev/post/2016-03-29-fake-people-better-wwbd/ <p>Think of a fictional character that you admire. It could be from a movie, a book, or maybe your favorite video game.</p> <p>Pick a character that brings you joy. Someone who is entertaining to watch and read about. This character should have a life that is a great story, since that&rsquo;s what the movie/book/game is about.</p> <p>Most importantly pick a role model, someone who you want to emulate. Someone who is happy, successful, and a good person.</p> <p>Got someone in mind?</p> <p>Good.</p> <p>Now here&rsquo;s my question to you. If that person was in your shoes, with all of their character traits intact but with all of your knowledge, experience, skills etc., what would they do?</p> <p>Do you think they&rsquo;d act any different from the way you&rsquo;d normally act? Do you think they&rsquo;d stay locked in the rut you&rsquo;re currently in, going about the same day to day routine?</p> <p>Of course not, <em>that&rsquo;s why you admire them in the first place.</em></p> <p>The image you&rsquo;ve created of this fictional character wouldn&rsquo;t do the same thing you&rsquo;re doing. They would do something interesting, something epic. They would break out of the daily monotony and create a remarkable story.</p> <p>They would embark on a training montage to be the best [warrior/athlete/chef/author/fashionista] in the world.</p> <p>They would spend their free time honing their talents in order to defeat their enemies, whether that enemy is The Capital, Voldemort, or a Sith Lord. All of their energy dedicated to a single purpose, their ultimate victory.</p> <p>What they certainly wouldn&rsquo;t do is spend hours a day watching stories about other people, yet that&rsquo;s what Americans do. According to the <a href="http://www.bls.gov/TUS/CHARTS/LEISURE.HTM" class="external-link" target="_blank" rel="noopener">American Bureau of Labor Statistics</a>, this chart below is the breakdown of time people spent enjoying leisure activities.</p> <p><em>[Note that this leisure activities split was determined via a survey and interviews, so I expect that the &lsquo;Watching TV&rsquo; and &lsquo;Playing Games&rsquo; times are an underestimate of reality. I&rsquo;ve seen other sources estimate as high as 4-5 hours of TV a day per American.]</em></p> <p><img src="https://jackson.dev/2016/03/leisure.jpg" alt="Leisure time on an average day"></p> <p> </p> <p>I mean honestly, can you imagine your favorite character sitting down and wasting their precious time like you do? I highly doubt you&rsquo;ve read or watched a story about them doing so.</p> <p>That free time is time that they could spend <em>doing</em> something.</p> <p>We waste all of this time living in fictional worlds that we don&rsquo;t take the time to improve our own lives, and that&rsquo;s the ultimate problem I want to address with this exercise. Let&rsquo;s use these fictional role models to combat the issue that they in part create.</p> <h1 id="akrasia"> Akrasia <a class="heading-link" href="#akrasia"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>That feeling you get when you&rsquo;re watching TV but you know that you should be doing something useful is called <em>akrasia</em>.</p> <p>Akrasia is &rsquo;the state of acting against your better judgment.&rsquo; It is sometimes described as the lack of self-control. And it&rsquo;s more than just procrastination.</p> <p>It&rsquo;s there every time you <a href="https://jackson.dev/post/2016-02-02-portion-control" >eat one more cookie</a>, or <a href="https://jackson.dev/post/2015-06-procrastination-a-case-study" >procrastinate on a big project</a>. It&rsquo;s the hesitation you feel when you&rsquo;re doing something you know is bad but you do it anyways.</p> <p>Once you learn to recognize when you are behaving akratically, you can learn to…well…stop. You can do that with your <a href="https://jackson.dev/post/2016-01-19-willpower-is-a-limited-resource" >limited willpower</a>, or you can cheat and do it by changing your perspective.</p> <h1 id="a-fresh-perspective"> A Fresh Perspective <a class="heading-link" href="#a-fresh-perspective"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p><em>“True character is revealed in the choices a human being makes under pressure – the greater the pressure, the deeper the revelation, the truer the choice to the character&rsquo;s essential nature.”</em><br> ― Robert McKee, <a href="https://en.wikipedia.org/wiki/Robert_McKee" class="external-link" target="_blank" rel="noopener">Story: Style, Structure, Substance, and the Principles of Screenwriting</a></p> <p>That quote is from a book on story creation. It&rsquo;s advice to writers on how to create great characters that their audiences will appreciate and connect with.</p> <p>But doesn&rsquo;t it make sense for real life advice too?</p> <p>There&rsquo;s a lot to take away from fictional characters that can be beneficial in real life, and you certainly don&rsquo;t have to read books on story creation or <a href="https://en.wikipedia.org/wiki/Monomyth" class="external-link" target="_blank" rel="noopener">the hero&rsquo;s journey</a> to apply it.</p> <p>Characters are simplistic, two-dimensional representations of reality. They have a small number of defining characteristics that they embody or seek to embody over the course of their story arc. While a simplistic character can sometimes be too simple for the purposes of a story, for our purposes that can actually be a good thing.</p> <p>Take your character from above. In fact, take any person you look up to even if they&rsquo;re not fictional. You could use a mental model of Elon Musk just as easily as a mental model of Gandalf.</p> <p>Now ask yourself this: <strong>If your role model character was in your shoes, right now, what would they do differently?</strong></p> <p>Often I find that it&rsquo;s easier to think of what someone else would do than to think about the situation directly. Mental models of other people are simpler than reality, so analyzing complex situations through their lens can make those difficult decisions easy.</p> <p>Over time, you should try to replace this mental model individual with Future You, your mental model of yourself in the future. If you already have a clearly defined model of the Future You that you want to be, use that instead.</p> <p>Painting issues in this way can be useful for cutting through to the core of a decision. Your role models don&rsquo;t quit when things get tough, they don&rsquo;t compromise their virtues for personal gain, they don&rsquo;t keep smoking when they know they should stop.</p> <p>They simply do what needs to be done according to their values, simplistic as they are.</p> <p>So the next time you&rsquo;re pouring yourself another drink, or pulling out another cigarette, or procrastinating on a big project, consider this: What Would Batman Do? (WWBD?)</p> <p> </p> <hr> <p>I&rsquo;m fascinated by stories and their implications. Why people (myself included) prefer to escape into a story instead of staying put in their own life.</p> <p>After all, the people you look up to in stories usually have no need to do that. Ever seen Luke Skywalker read a fiction book?</p> <p>Why must we escape to new Galaxies, or fake histories, or real life other people instead of spending time in our own lives?</p> <p>I&rsquo;ve always thought it would be fun to have a &lsquo;storybook&rsquo; style life. Everything is straightforward, there&rsquo;s one &lsquo;Big Bad&rsquo; to compete against and thus there is little ambiguity in the everyday decisions to be made.</p> <p>Reality is obviously different. Usually there&rsquo;s not a &lsquo;Big Bad&rsquo; and the options are limitless. We&rsquo;re in an <a href="https://en.wikipedia.org/wiki/Open_world" class="external-link" target="_blank" rel="noopener">open world</a> world and there&rsquo;s no limit to what we can do.</p> <p>This website is supposed to be about that to some extent. Using science to engineer a remarkable life, an interesting life, a lifestyle chosen and designed because you want it.</p> <p>The past few months I have been researching and writing about useful knowledge, mental models, and techniques for self-improvement. I&rsquo;m attempting to slowly build a toolset for lifestyle design that anyone can use with some effort.</p> <p>What&rsquo;s missing is the story.</p> <p>To that end I&rsquo;m going to begin sharing my story. I&rsquo;m not saying that I possess a remarkable story that should be emulated, quite the opposite really, but I don&rsquo;t intend to talk much about the past.</p> <p>What I intend to document is the story going forward. The story of using myself as a guinea pig for implementing this toolset of knowledge. I&rsquo;ll publish my &lsquo;N of 1&rsquo; experiments that I carry out on myself, and share the preparation and results of any adventures I may go on.</p> <p>The biggest changes and greatest moments of my life have come from experimentation and adventure, and I plan to make some more memories.</p> <p>Quite possibly, this will be a story with a lot of failures. But that&rsquo;s ok. The only people who have never failed have never tried.</p> <p>If you&rsquo;d like to follow along with me, feel free to subscribe to the RSS feed or via email at the top of the page.</p> Delusional Beliefs https://jackson.dev/post/2016-03-22-delusional-beliefs/ Tue, 22 Mar 2016 00:00:00 +0000 https://jackson.dev/post/2016-03-22-delusional-beliefs/ <p>Today I&rsquo;m going to talk about why people believe stupid things. Like <em>really</em> stupid things. Things that are so <em>clearly</em> wrong that these people must have something wrong with their brains.</p> <p>By that I mean that this is another article on <a href="https://jackson.dev/post/2016-01-26-cognitive-biases/" >Cognitive Biases</a>. And by &rsquo;these people&rsquo; I mean everybody.</p> <p>Since the US in currently in the midst of its presidential primary election season, beliefs and the art of persuasion has been on my mind. The sheer distance between what each side of the aisle believes is mind boggling, which if you think about it is a bit odd.</p> <p>We&rsquo;re all exposed to the same information, the same news articles, the same <em>facts</em>. And yet, we come to vastly different conclusions.</p> <p>Today I&rsquo;m going to try to explain why.</p> <h1 id="confirmation-bias"> Confirmation Bias <a class="heading-link" href="#confirmation-bias"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>The first of the biases I&rsquo;ll cover in this article, and likely the most well-known. The other effects that I will discuss are types of confirmation bias.</p> <p>Confirmation bias is the tendency towards preferring what we already believe. While we&rsquo;d like to think that we get exposure to all different sides of an issue, we really don&rsquo;t.</p> <p>Because of your confirmation bias, you will tend to seek out information that confirms your beliefs. Few people watch both Fox and MSNBC, as the common view is that they are biased to the right and left respectively. (You can&rsquo;t escape this even if you try, as Google personalizes their search results based on your personal interests.)</p> <p>When you see new information, you will interpret that more favorably for your currently held beliefs. You&rsquo;ll make excuses for the things that contradict your view and take supporting information as gospel.</p> <p>You will actually <em>remember</em> the information that confirms your previously held beliefs better than contradictory information. That sounds nuts, but if you&rsquo;ve read my article on <a href="https://jackson.dev/post/2016-02-16-false-memory" >False Memory</a> it shouldn&rsquo;t sound like much of a stretch. By remembering supporting arguments better than opposing ones, then from your perspective your views will always be the most supported.</p> <p>This causes us to become very insular in the information we pay attention to, and the people we associate with. You stop reading and watching things that don&rsquo;t confirm your viewpoints. You stop exposing yourself to other information because it&rsquo;s uncomfortable. And it&rsquo;s difficult to be friends with someone when they hold a belief that you honestly believe is delusional.</p> <p>You may like to claim that you&rsquo;re open-minded, but you&rsquo;re probably not. Without breaking through this bias you are destined to hold the same beliefs forever.</p> <h1 id="backfire-effect"> Backfire Effect <a class="heading-link" href="#backfire-effect"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>One of the effects of the confirmation bias is…surprising. And surprisingly prevalent, so I need to call it out and explain it.</p> <p>When presented with evidence against their beliefs, some people will change their views in such a way as to <em>strengthen their already held beliefs.</em> This is beyond just ignoring the new information: presenting a liberal with facts supporting the conservative argument will push them to become more liberal, not more moderate.</p> <p>I can&rsquo;t overstate this enough. Someone who believes that the Earth is flat will, when shown a picture of the spherical Earth as seen from space, react by throwing out the evidence and believing even more strongly that the Earth is definitely flat.</p> <p>It defies logic, but you usually can&rsquo;t change someones beliefs by facts alone. I can sit around all day and explain to you why the Earth is clearly planet-shaped, but if you were firmly locked into your belief that it was flat when we started, you will only be more entrenched in your belief when we finish</p> <p>As an example a little closer to home, here is the study that first described the backfire effect (as far as I can tell at least) [<a href="http://www.dartmouth.edu/~nyhan/nyhan-reifler.pdf" class="external-link" target="_blank" rel="noopener">PDF</a>]. It describes how conservatives were presented with a fake article containing a quote from George W. Bush claiming the existence of weapons of mass destruction in Iraq. Some of the subjects were shown a factual correction pointing out that the quote was false and no WMDs existed. The people that read the factual correction were measured afterwards to hold stronger beliefs that the initial quote was true, despite the article pointing out the mistake.</p> <p>Their beliefs were strengthened by a factually correct argument against them.</p> <h1 id="recognizing-tactics-of-persuasion"> Recognizing Tactics of Persuasion <a class="heading-link" href="#recognizing-tactics-of-persuasion"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Watching politics allows us to see persuasion techniques in action. You can observe politicians that agree with your beliefs and those that disagree, and watch how the best of them work to change your belief to their side.</p> <p>Importantly, you should observe how you respond to the arguments as well. Do you tend to give more weight to the arguments supporting your views?</p> <p>Probably. So politicians try to use other means to persuade you to their side. When you start to look at them objectively, they stratify into a few different categories.</p> <p>Some politicians are terrible, and their main tactic used are &lsquo;gotchas&rsquo;. Obviously that won&rsquo;t work, that&rsquo;s pure divisiveness; it will secure your base but not win over anyone. It&rsquo;s like trying to convince an Englishman that the royal family isn&rsquo;t important by insulting the queen. You just don&rsquo;t do it.</p> <p>A better politician will try to convince you that their position is defensible by using facts. Most would think that this <em>should</em> work, but as we&rsquo;ve seen above it doesn&rsquo;t. People ignore facts in favor of previously held beliefs.</p> <p>A great politician already knows those don&rsquo;t work. So they use other means.</p> <p>A great manipulator* focuses on convincing you with rhetoric instead of facts. Often they don&rsquo;t even try to convince you of specific issues, they just work to get you to believe that they themselves are trustworthy. Once they&rsquo;ve given you a new belief, (how trustworthy and capable they are), then they tell you what the correct views are. If your view that they are trustworthy and capable is stronger held than your belief in whatever it is they&rsquo;re trying to convince you of, they&rsquo;ve won.</p> <p><em>*Yes, manipulator has negative connotations, but I don&rsquo;t intend it that way.</em></p> <p>They know that people hear what they want to hear, so they employ strategic ambiguity to make statements on divisive topics that everyone would agree with. <a href="http://blog.dilbert.com/post/140272615821/strategic-ambiguity-master-persuasion-series" class="external-link" target="_blank" rel="noopener">Here are some examples describing how Donald Trump is using that strategy.</a></p> <p>They repeat themselves over and over because the more times you hear it the more likely you are to believe it&rsquo;s true. That&rsquo;s the Illusory Truth Effect, another cognitive bias.</p> <p>They use a limited vocabulary to talk to the public, because using simpler language in an argument sounds more persuasive.</p> <p>If you know what to look for, watching politics is like watching a competitive sport. It&rsquo;s a competition to see who is the best manipulator.</p> <h1 id="my-beliefs"> My Beliefs <a class="heading-link" href="#my-beliefs"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>I am of the belief that this is a problem.</p> <p>I don&rsquo;t <em>necessarily</em> have a problem that others succumb to these tendencies, but I don&rsquo;t want to be subject to them myself. I don&rsquo;t want to be convinced with rhetoric and manipulation; I want my viewpoints to be closer to objective reality. Sound science and defensible logical inference should be the base upon which I build my views and beliefs.</p> <p>Hopefully you feel the same.</p> <p>Combating it is difficult though. You need to pay attention to when you have no real facts backing up your argument. You need to recognise when you are experiencing cognitive dissonance: believing two contradictory things.</p> <p>Those are probably signs you need to take a step back and reevaluate.</p> <h1 id="cracking-your-naive-realism"> Cracking Your Naive Realism <a class="heading-link" href="#cracking-your-naive-realism"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>The whole point of this article is to try to break through your perception of Naive Realism. In fact, that&rsquo;s really the point of my whole Cognitive Bias series.</p> <p><a href="https://en.wikipedia.org/wiki/Na%C3%AFve_realism_%28psychology%29" class="external-link" target="_blank" rel="noopener">Naive Realism</a> is the belief that we see the reality around us objectively, and that people who disagree must be uninformed, irrational, or intentionally biased.</p> <p>Hopefully by understanding how you (and others) can believe in something delusional while still being a rational person you can start to challenge your long-held belief that you are always correct.</p> <h1 id="homework"> Homework <a class="heading-link" href="#homework"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>So here&rsquo;s a question for you. And I want you to <em>really</em> think about it.</p> <p>What are you convinced of that may be wrong?</p> <p>I can guarantee you have a viewpoint that is blatantly opposed to the viewpoint of some large group of people, and those people can&rsquo;t <em>all</em> be delusional.</p> <p>Perhaps you believe that climate change is not man-made, <a href="http://climate.nasa.gov/scientific-consensus/" class="external-link" target="_blank" rel="noopener">despite the scientific consensus</a>. Perhaps you think that the Earth is &lsquo;planet-shaped&rsquo; <a href="http://www.theflatearthsociety.org/tiki/tiki-index.php" class="external-link" target="_blank" rel="noopener">despite all evidence to the contrary</a>. Perhaps you think vaccines are safe, <a href="http://howdovaccinescauseautism.com/" class="external-link" target="_blank" rel="noopener">despite this website describing how vaccines cause autism</a> (<a href="https://www.aap.org/en-us/Documents/immunization_vaccine_studies.pdf" class="external-link" target="_blank" rel="noopener">with sources!</a>).</p> <p>Maybe you think that democracy is the best form of government, <a href="http://www.businessinsider.com/major-study-finds-that-the-us-is-an-oligarchy-2014-4" class="external-link" target="_blank" rel="noopener">or that the U.S. is one</a>.</p> <p>The thing is, what if you&rsquo;re wrong?</p> <hr> <p> </p> <p><em>If you&rsquo;d like to read more about divisiveness in politics and possible solutions to the problem, check out <a href="https://en.wikipedia.org/wiki/Charles_Wheelan" class="external-link" target="_blank" rel="noopener">The Centrist Manifesto</a>.</em></p> <p><em>Image courtesy of <a href="http://www.laughandpee.com/" class="external-link" target="_blank" rel="noopener">Ryan McGuire</a>.</em></p> The Pareto Principle https://jackson.dev/post/2016-03-15-the-pareto-principle/ Tue, 15 Mar 2016 00:00:00 +0000 https://jackson.dev/post/2016-03-15-the-pareto-principle/ <p>120 years ago, an Italian economist named Vilfredo Pareto discovered a similarity between the landowners of Italy and the peas growing in his garden. This observation led to a now common rule of thumb in business known as the Pareto principle.</p> <p>You may have heard it called <strong>the 80-20 rule.</strong></p> <h1 id="the-observation"> The Observation <a class="heading-link" href="#the-observation"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Pareto made his initial observation by looking at the distribution of land ownership in Italy. What he found was that 80% of the land was owned by 20% of the people. Not a surprising observation in itself, but when he looked at <em>other</em> countries, he found a nearly identical distribution of ownership.</p> <p>In his garden, Pareto observed that 20% of his peapods produced 80% of the peas. An oddly similar distribution. Why would these unrelated observations be so similar?</p> <p>In fact, there are examples of this ratio from every facet of life.</p> <ul> <li>The richest 20% of the world takes in 82% of the income. [<a href="https://www.imf.org/external/pubs/ft/fandd/2001/12/wade.htm" class="external-link" target="_blank" rel="noopener">Source</a>]</li> <li>In the United States, 20% of healthcare patients use 80% of the resources.</li> <li>Microsoft reports that 20% of their known bugs are responsible for ~80% of their crashes. [<a href="http://www.crn.com/news/security/18821726/microsofts-ceo-80-20-rule-applies-to-bugs-not-just-features.htm" class="external-link" target="_blank" rel="noopener">Source</a>]</li> <li>and so on&hellip;</li> </ul> <p>As I&rsquo;m sure you&rsquo;ve guessed, there&rsquo;s nothing weird going on here. There&rsquo;s no black magic forcing this into place. The reality, obviously, is much more mundane.</p> <h1 id="statistics"> Statistics <a class="heading-link" href="#statistics"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Ok, maybe it&rsquo;s a little bit like black magic.</p> <p>This effect comes about due to nothing more than a power law relationship between the two quantities, where &ldquo;a relative change in one quantity results in a proportional relative change in the other.&rdquo; Skipping the math, what this really means is that the two quantities look like this if you graph them against each other.</p> <p><img src="https://jackson.dev/2016/long-tail.svg" alt="Power law distribution"> [<a href="https://en.wikipedia.org/wiki/Power_law" class="external-link" target="_blank" rel="noopener">Image courtesy of Wikipedia&rsquo;s Power Law article</a>]</p> <p>You have likely seen a graph or two with this type of distribution before. The green part of the graph represents only 20% of the x-axis but accounts for 80% of the total area under the curve.</p> <p>In actuality, there isn&rsquo;t anything special about this specific ratio. &lsquo;The 80-20 rule&rsquo; as a saying is easy to remember, but it&rsquo;s just the split that arises from a standard distribution of results. It could just as easily be &rsquo;the 80-10 rule&rsquo;, the gist is the same: a small percentage of actions produce a disproportionately large portion of the results.</p> <h1 id="what-this-means"> What This Means <a class="heading-link" href="#what-this-means"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>This rule is well known in the business world for its applicability as a rule of thumb for optimization. It&rsquo;s valuable because it doesn&rsquo;t just apply to one thing; as we&rsquo;ve seen above it applies to <em>many</em> different areas.</p> <p>If you&rsquo;re a knowledge worker, I can almost guarantee that you spend a small amount of your working time actively working on things that are responsible for the majority of your results. Your job performance depends on getting great results at a few particular things and getting by on the rest.</p> <p>Most of the emails you send are probably unimportant, but a few of them may be vital. If you spend all day responding to email (or in meetings) you&rsquo;re <em>probably</em> doing something wrong.</p> <p>Similarly, most of the studying that you do is probably worthless. If you&rsquo;re trying to learn a new language, for example, you shouldn&rsquo;t be spending time memorizing esoteric vocabulary. Native English speakers probably have a vocabulary of ~15,000 words, but the most common 1,000 words account for greater than 80% of the spoken language. The top 100 words are a full 50% of all English spoken. If you&rsquo;re bothering to learn the word for &lsquo;apple&rsquo; before you learn those, you&rsquo;re doing something wrong.</p> <p>Learn the grammar and the most common words first and you&rsquo;re on your way to learning a new language. All it takes is a small amount of research and planning.</p> <h1 id="ok-so-what"> OK, So What? <a class="heading-link" href="#ok-so-what"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Amusingly, while the upper part of this article is good background it isn&rsquo;t entirely necessary to our conclusion. While I spent a while expounding upon why it&rsquo;s a thing, the entire article can be summarized in 2 sentences.</p> <p>Most of the things you do have little effect on your desired outcome. A small percentage of your actions (maybe 20%, maybe less) contribute to the majority (80%+) of the results.</p> <p>This leads to the single most useful rule in productivity: <strong>Think about the actions you take <em>before</em> you do them.</strong></p> <p>Figure out what has the largest effect on the outcome you want, and reduce everything else. Ruthlessly eliminate the work that is inefficient and ineffective at getting to your desired result. There are better ways to spend your time.</p> <hr> <p><em>This topic is a core principle of the best business book I&rsquo;ve read in the past year: <a href="https://en.wikipedia.org/wiki/Deep_Work" class="external-link" target="_blank" rel="noopener">Deep Work</a> by Cal Newport (who also runs the <a href="http://calnewport.com/blog/" class="external-link" target="_blank" rel="noopener">Study Hacks blog</a>). The entire book is about the importance of maximizing time on your most impactful work, and on strategies you can take to do so. I highly recommend it.</em></p> Misplaced Fear https://jackson.dev/post/2016-03-08-misplaced-fear/ Tue, 08 Mar 2016 00:00:00 +0000 https://jackson.dev/post/2016-03-08-misplaced-fear/ <p>I&rsquo;ve noticed a disturbing trend recently. Not that it&rsquo;s a recent trend, I&rsquo;ve just begun to notice it.</p> <p>People are irrationally afraid of things that they will likely never experience.</p> <p>As an example, let&rsquo;s look at terrorism. More people seem to be afraid of getting killed in a terrorist attack than they are of being killed by cancer. That fear is mind-bogglingly irrational.</p> <p>If you live in the western world, you are more likely to die from almost anything else than you are to be killed in a terrorist attack. Here are some statistics I&rsquo;ve compiled from data about the United States in particular. I&rsquo;ve rounded off most of the numbers since there tends to be a bit of ambiguity in the sources.</p> <ul> <li>Since 2001, &lt;4k people have died from terrorist attacks (The vast majority being the 2,977 people who died in the 9/11 attacks)</li> <li>303 Americans were killed by religious extremists in the decade from 2004 to 2014 <ul> <li>24 of those were on US soil</li> </ul> </li> </ul> <p>In the same decade&hellip;</p> <ul> <li>100 people were killed by vending machines</li> <li>500 people were killed by lightning</li> <li>6,000 people died from autoerotic asphyxiation</li> <li>300,000 people were killed by guns (the majority of which were suicides)</li> <li>500,000 people were killed in car accidents</li> <li>610,000 people died from heart disease&hellip;every year</li> </ul> <p>If you look at it objectively, it&rsquo;s obviously not something to worry about. But we still do.</p> <p>It&rsquo;s a fear of the unknown. A worry that you don&rsquo;t know when or where the big bad evil person will come for you. That you could be going about your day and someone could come along and kill you just because they believe it&rsquo;s the right thing to do. They might even believe in a perversion of <em>your own religion</em>, yet still want to kill you to support their own goals. It&rsquo;s a scary thought.</p> <p>But why is it scary? You&rsquo;re 1000 times more likely to die in a car accident today than you are to get killed by a terrorist, but you&rsquo;re probably not scared of that happening.</p> <p>We&rsquo;re too concerned with fear of the flashy things. Our minds focus on the things that are vivid and interesting, and not on the things that are most likely to happen.</p> <p>It&rsquo;s an irrationality, one that you should rigorously work to beat out of yourself.</p> <h1 id="what-should-scare-you"> What Should Scare You? <a class="heading-link" href="#what-should-scare-you"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Statistics are not trusted by a lot of people; they can be massaged to say just about anything. However, statistics viewed properly can reveal the truth.</p> <p>If you find yourself scared of something that&rsquo;s obviously irrational, like a terrorist attack, look at the statistics. What is the most likely thing that&rsquo;s going to kill you? Obviously terrorist attacks are low on the list, hell, it&rsquo;s below lightning. (Incidentally, while looking this up I learned that lightning only kills you 10% - 30% of the time, so lightning isn&rsquo;t all that bad.)</p> <p>Here&rsquo;s a nice little graph I borrowed from Wikipedia that shows the leading causes of death by age.</p> <p><img src="https://jackson.dev/2016/causes-of-death-by-age-group.png" alt="Causes of death by age group"></p> <p>To figure out what&rsquo;s most likely to kill you, you need a little bit of Calculus. Since you&rsquo;ve already lived to whatever age you are now, ignore everything to the left of your age group. For each cause of death, add up the area under the curve to the right of your age group and see which comes out to be the most likely cause of death for the rest of your life. Now this is a real challenge, so if you need to go review your old Calculus textbooks, I can wait.</p> <p>Ok, now, raise your hand if you got terrorism. Anybody?</p> <p>That would be pretty impressive, considering it&rsquo;s <em>not even on the damn list</em>.</p> <p>In fact, you&rsquo;d have to be planning to kill yourself before 75 for you to get anything other than Cardiovascular Disease as your answer.</p> <h1 id="your-heart-will-kill-you"> Your Heart Will Kill You <a class="heading-link" href="#your-heart-will-kill-you"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>I&rsquo;m not <em>trying</em> to be depressing here, I promise I&rsquo;m trying to help.</p> <p>Part of what makes terrorism scary, as I&rsquo;ve discussed before, is the feeling that you can&rsquo;t do anything about it. You&rsquo;re scared that somebody else will come and take away your opportunity at living for their own reasons. I hear you, I don&rsquo;t want to get murdered either.</p> <p>But luckily for most of us, that&rsquo;s not going to ever be a problem.</p> <p>Far and away more likely, your heart is going to give out. Or you&rsquo;ll die from cancer (that&rsquo;s the second one on the list by the way, &ldquo;Malignant neoplasms&rdquo;).</p> <p>And these things, the things that likely will kill you, are <strong>preventable</strong>.</p> <p>Not entirely, obviously. I&rsquo;m not some crackpot running around preaching that cancer is curable by eating broccoli once a day (though to be fair I did point out that <a href="https://jackson.dev/post/2016-02-23-the-benefits-of-cold-showers/" >Cold Showers <em>might</em> help fight tumors</a>, the key word being <em>might</em>). But you can reduce your chances of getting heart disease or cancer in the first place through lifestyle changes.</p> <p>Even though you can&rsquo;t cure them you <em>can</em> reduce your odds of getting them or delay the onset with relatively little effort. By acting now you might add years of healthy living to your lifespan.</p> <h1 id="how-to-live-forever"> How to Live Forever <a class="heading-link" href="#how-to-live-forever"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>(As much as that heading is a joke, only 91% of all <em>homo sapiens</em> who have ever lived have actually died)</p> <p>You already know what I&rsquo;m going to say here, but I&rsquo;m going to say it anyways.</p> <p><em>The odds of dying from heart disease, cancer, type II diabetes, and Alzheimer&rsquo;s can all be reduced through proper diet and exercise.</em></p> <p>While everyone else is worried about getting killed by other people, people who pay attention to statistics are over here eating right and exercising. There&rsquo;s never a guarantee in life, but taking care of yourself has the potential to add <em>years</em> of healthy living to your life.</p> <p>In contrast, worrying about a violent attack is more likely to just increase your blood pressure and kill you sooner.</p> <p>So don&rsquo;t let the terrorists win. Eat your veggies.</p> The Anchor Habit https://jackson.dev/post/2016-03-01-the-anchor-habit/ Tue, 01 Mar 2016 00:00:00 +0000 https://jackson.dev/post/2016-03-01-the-anchor-habit/ <p>The fatal mistake made by almost everyone when trying to improve themselves is to take on too many things at once. You see this all the time in people&rsquo;s New Year&rsquo;s Resolutions.</p> <ul> <li>I&rsquo;m going to eat healthier</li> <li>AND I&rsquo;m going to workout everyday</li> <li>AND I&rsquo;m going to finish that knitting project</li> <li>AND I&rsquo;m going to journal everyday</li> <li>AND …</li> </ul> <p>It doesn&rsquo;t work. Few people are able to force themselves to make all of those changes at once. If you made such a list as your New Years resolutions, tell me: Do you even <em>remember</em> the things you planned to do?</p> <p>I&rsquo;m betting the answer is no.</p> <p>Luckily there&rsquo;s an easier way. One that doesn&rsquo;t involve the ability to use up an extreme amount of <a href="https://jackson.dev/post/2016-01-19-willpower-is-a-limited-resource/" >willpower</a>.</p> <h1 id="the-ultimate-secret-to-forming-habits"> The Ultimate Secret to Forming Habits <a class="heading-link" href="#the-ultimate-secret-to-forming-habits"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Ok, here it is. The big secret. The one people buy all those fancy self-help books to learn. You ready?</p> <p>When you want to form a new set of habits (or get rid of some bad ones) <strong>pick one, and <em>only one</em></strong> and do that first. Do that alone for a minimum of 3 weeks. Then you can add another one.</p> <p>If two of your habits complement each other, only then can you try to do two at once. For example, I talked last week about taking <a href="https://jackson.dev/post/2016-02-23-the-benefits-of-cold-showers/" >Cold Showers</a>. Since I had seen that several other people reported it to have much the same calming effect as meditation I paired it with a renewed interest in a daily meditation practice, which worked surprisingly well.</p> <p>Repeat until you&rsquo;ve acquired all of the things you wanted. Or gotten rid of all the bad habits you&rsquo;ve picked up over the years. Or until you&rsquo;re dead, that works too.</p> <p>Trying to expend your energy on too much at once depletes your willpower and makes you fail all of them. It just isn&rsquo;t going to work in most circumstances.</p> <p>So now you have a list of things you want to change about yourself, but you are only allowed to do one at a time. They all look so exciting…where do you start?</p> <h1 id="the-anchor-habit"> The Anchor Habit <a class="heading-link" href="#the-anchor-habit"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Introducing big secret #2, the Anchor Habit. I even capitalized it, so it must be important.</p> <p>An Anchor Habit is a habit that helps you form other habits. One that helps you do better in your life just from that one single change. It is turbofuel for changing your life, and a base upon which you can grow a better you.</p> <p>Wow, that sounded cheesy.</p> <p>For me, the most important determination of whether I will have a good day or a bad one is my Anchor Habit. On the days I meet my habit, I am almost twice as productive as days that I don&rsquo;t, and it is rare that I have a bad day when I do this one thing. For my life, it is far more important than any other single thing I do.</p> <p>My prime Anchor Habit is good sleep and early rising.</p> <p>If I wake up before 8 am (before 7 is even better) after a good 8 or more hours of sleep, I will have a good day. If I don&rsquo;t meet both of those, then my day is a bit of a toss up. Either I&rsquo;m too groggy to get myself to do what is necessary, or I waste time getting distracted because of my late start to the day.</p> <p>Getting an early start to the day is my rocket fuel. I can immediately get time to work for myself before I have to start dealing with other people. I can get some solid work done during the early hours of the morning while everybody else is eating breakfast or still sleeping.</p> <p>I&rsquo;m usually not a very productive person, admittedly, but when I&rsquo;m out of bed at 6 am with a full nights rest, that&rsquo;s almost guaranteed to be the most productive day of the week. So when I get off track in my work habits, getting back on track is as simple as changing one thing: my sleep. Everything else I need to do follows from that.</p> <h1 id="finding-your-rocket-fuel"> Finding Your Rocket Fuel <a class="heading-link" href="#finding-your-rocket-fuel"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>But that&rsquo;s just me. Maybe you already have sleep dialed down, or maybe you have young kids and never get to sleep a full night anyways. Your Anchor Habit is probably something entirely different.</p> <p>If it&rsquo;s not obvious you&rsquo;ll probably need to spend a little time trying things out or thinking about it. I promise it&rsquo;s a worthwhile endeavor.</p> <p>If you need help, take a look at your ultimate goals. I assume that you have a bucket list, or short term goals, or some habits that look cool that you&rsquo;ve been thinking about taking up. Something of that sort. Even if you have a long list, I&rsquo;m betting that there are actually very few themes. You likely have only a few different themes of things in your life you want to do or change: improving your health, traveling more, better personal relationships, success in your work, etc.</p> <p>Each of these broad themes have one or two habits that get you 80% of the way there. For example</p> <ul> <li>Want to improve your health? Upgrade your diet, one step at a time.</li> <li>Want to travel more? Save up money and/or travel rewards points.</li> <li>Want better personal relationships? Talk to your loved ones more often.</li> <li>Want success in work? Work smarter (on the right things) then work harder (be more productive).</li> </ul> <p>Hopefully in looking at your own list you can identify a common thread that you can build a base upon.</p> <p>In my example, I am trying to be more productive in my professional life and my personal life, and I found that the best way to do that is just to get better sleep and start my day earlier. It sounds simple, but literally the most important part of my day actually happens the night before, when I choose when to go to sleep.</p> <p>Don&rsquo;t try to do everything at once. Focus on the most important of your habits, the Anchor Habit, and the rest will follow.</p> The Benefits of Cold Showers https://jackson.dev/post/2016-02-23-the-benefits-of-cold-showers/ Tue, 23 Feb 2016 00:00:00 +0000 https://jackson.dev/post/2016-02-23-the-benefits-of-cold-showers/ <p>For the past month I&rsquo;ve been foregoing my usual steaming hot shower for a cold one. Not everyday, that habit is a hard one to break, but the majority of the time I voluntarily froze myself in pursuit of science.</p> <p>I try a lot of crazy shit on myself, so let me answer the usual questions even before we get started: Yes, it&rsquo;s difficult and uncomfortable. No, I&rsquo;m not crazy. Yes, I have very good reasons. Yes, I&rsquo;ll probably keep doing it. No, I never regretted my decision.</p> <p>Ok, you got me, that last one was a lie.</p> <h1 id="why-am-i-doing-this-to-myself"> Why Am I Doing This to Myself? <a class="heading-link" href="#why-am-i-doing-this-to-myself"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Ah, the eternal question. It&rsquo;s good to know the answer before you try doing something distinctly uncomfortable, like freezing your ass off, <em>voluntarily</em>.</p> <p>With a good enough <em>Why?</em>, you can push through almost anything. The more painful something is the more important it must be. The more potentially beneficial.</p> <p>Giving up your favorite dessert will help you reach your goal weight, but that&rsquo;s pretty easy. Going through chemotherapy is much harder, but it might save your life.</p> <p>The trials need to be worth it in the end.</p> <p>Lucky for me, I don&rsquo;t take cold showers because I woke up one day and decided to be a masochist. There are very good reasons for putting myself through that, too many to get to in this post even.</p> <p>And thankfully, after several <em>distinctly uncomfortable</em> days, I&rsquo;ve even started to enjoy it.</p> <h1 id="the-benefits"> The Benefits <a class="heading-link" href="#the-benefits"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>There is an abundance of research on the benefits of cold showers. Much of it is on longer term exposure, but some has looked into cold showers in particular. Here are some of the most well-documented benefits:</p> <h3 id="treatment-for-depression"> Treatment for Depression <a class="heading-link" href="#treatment-for-depression"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>At least <a href="http://www.ncbi.nlm.nih.gov/pubmed/17993252" class="external-link" target="_blank" rel="noopener">one study</a> has shown that cold showers are a potential treatment for depression. The theory is that due to the high density of cold receptors in the skin, a cold shower sends an overwhelming amount of electrical impulses from peripheral nerve endings to the brain, which could result in an anti-depressive effect.</p> <h3 id="relieve-pain"> Relieve Pain <a class="heading-link" href="#relieve-pain"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Cold hydrotherapy is known to reduce pain. It&rsquo;s a stress-induced analgesic (pain reliever) for the same reason it is a treatment for depression. The heightened nerve signals &lsquo;crowd out&rsquo; the pain signals, and the effect persists after the exposure.</p> <h3 id="more-brown-fat-less-other-fat"> More Brown Fat, Less Other Fat <a class="heading-link" href="#more-brown-fat-less-other-fat"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Brown fat, or BAT (brown adipose tissue) is a type of fat that burns energy and glucose to generate heat. It&rsquo;s <em>good</em> fat, and you have a tiny amount of it, mostly concentrated in the neck/shoulder/upper back area. More brown fat boosts your metabolism, keeps you warmer throughout the day, and thus just like muscle it burns fat all day long.</p> <p>Fat that burns fat. Cool.</p> <p><a href="https://www.sciencedaily.com/releases/2014/06/140623091949.htm" class="external-link" target="_blank" rel="noopener">Long term cold exposure stimulates the growth of brown fat</a>. Each shower also stimulates those fat cells into action generating heat to warm you back up, so you are literally burning fat to keep you warm while you shower. I won&rsquo;t overstate it though, this benefit is probably only a few pounds a year from cold showers alone.</p> <h3 id="improved-insulin-sensitivity"> Improved Insulin Sensitivity <a class="heading-link" href="#improved-insulin-sensitivity"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>More brown fat improves your insulin sensitivity, which is a very good thing. Type II diabetes is caused by <em>decreased</em> insulin sensitivity. The authors of the paper above suggest that increasing your brown fat may be a promising therapeutic strategy for treating both diabetes and obesity. Even if you&rsquo;re not concerned about diabetes, higher insulin sensitivity is still a good thing.</p> <h3 id="reduces-fatigue"> Reduces Fatigue <a class="heading-link" href="#reduces-fatigue"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Cold stress has been shown to reduce the level of serotonin in most regions of the brain, which would be consistent with reduced fatigue according to animal models of exercise-related fatigue. <a href="http://www.ncbi.nlm.nih.gov/pubmed/17958903" class="external-link" target="_blank" rel="noopener">Source</a>.</p> <h3 id="better-immune-system"> Better Immune System <a class="heading-link" href="#better-immune-system"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Acute cold exposure (like a cold shower) is used in Siberia as a preventative measure against illnesses. Of course, being Siberia, they just run outside in swim wear and dump a bucket of water over their heads. It&rsquo;d be funny if it didn&rsquo;t work but for those that practice what they call &lsquo;dousing&rsquo;, 95% go through the flu season without getting sick compared to 75% of the others.</p> <p>I should mention that <a href="http://siberiantimes.com/healthandlifestyle/others/news/like-ducks-to-water-in-the-snow-keeping-kids-healthy-siberian-style" class="external-link" target="_blank" rel="noopener">the example I have is on children aged 2 - 6</a>. If a six year old can do it you probably can too.</p> <p>Additionally, from <a href="http://www.ncbi.nlm.nih.gov/pubmed/17999770" class="external-link" target="_blank" rel="noopener">this paper</a>, &rsquo;there is accumulating evidence that daily brief cold stress can increase both numbers and activity of peripheral cytotoxic T lymphocyctes and natural killer cells, the major effectors of adaptive and innate tumor immunity.&rsquo; Basically, the paper hypothesizes that brief cold-water stress every day for months could enhance anti-tumor immunity and improve survival rates for non-lymphoid cancer.</p> <p>Improve your cancer outcome just by turning the shower knob to the right instead of the left&hellip;maybe.</p> <h3 id="keeps-skin-and-hair-healthy"> Keeps Skin and Hair Healthy <a class="heading-link" href="#keeps-skin-and-hair-healthy"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Hot water dries out your skin and hair. Cold water closes your cuticles and pores, which can make your hair shinier and your skin look healthier. Pretty simple.</p> <h3 id="increase-testosterone"> Increase Testosterone <a class="heading-link" href="#increase-testosterone"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Cold exposure has been shown to increase testosterone production, at least in men. In addition to increasing libido, higher testosterone levels boost overall strength and energy levels.</p> <h3 id="and-more"> And More <a class="heading-link" href="#and-more"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>For a whole host of other benefits, read through <a href="http://www.ncbi.nlm.nih.gov/pmc/articles/PMC4049052" class="external-link" target="_blank" rel="noopener">this medical article</a> that cites many many other studies and reviews concerning hydrotherapy (water-based therapy). It&rsquo;s not exclusively about cold exposure, but a large part of the article covers that topic.</p> <h1 id="my-own-experience"> My Own Experience <a class="heading-link" href="#my-own-experience"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Anecdotally I&rsquo;ve experienced few of the benefits above (since it&rsquo;d be a bit difficult to measure, and a month is too short anyways) but I know that the cold showers make me <em>feel</em> better. After taking one, I feel refreshed in a way that I don&rsquo;t feel after a hot shower. I am noticeably warmer the rest of the day, so I&rsquo;m more comfortable with a lower temperature in my apartment.</p> <p>I also feel calmer and more aware of the present moment. It&rsquo;s similar to the clarity that comes after a meditation session, in fact I&rsquo;ve taken to doing my daily meditation after my shower most days to compound the effect. It seems to be related to the meditation practice in that it forces you to be aware of the present during the uncomfortableness of the shower, whereas in meditation the clarity of the present is done through focus.</p> <p>From taking cold showers relatively consistently over the past month, I can say that I actually have started to prefer taking non-hot showers rather than hot showers. Occasionally I just want to take a hot shower and revel in the warmth, so I do, but most days I take a cool/chilly shower and occasionally a truly cold one.</p> <h1 id="how-you-ask"> How, You Ask? <a class="heading-link" href="#how-you-ask"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h1> <p>Taking a cold shower is the simplest thing in the world. Get in the shower, and turn it on to full cold. Make sure you&rsquo;re standing under the nozzle, and just keep pushing through the shivering, hyperventilating phase until it passes.</p> <p>Have fun with that.</p> <p>While that might be a good test of fortitude to do occasionally, it sucks. It forces you to us a <em>lot</em> of <a href="https://jackson.dev/post/2016-01-19-willpower-is-a-limited-resource/" >willpower</a> to go through with it, and I&rsquo;ve found a much easier way.</p> <p>Here&rsquo;s the routine I&rsquo;ve taken up for shower time:</p> <ol> <li>[Optional] Warm Up. Taking a cold shower after getting warm, from a workout or just a few quick pushups, jumping jacks, or squats, is a lot easier than jumping in right out of bed.</li> <li>Turn on the shower.</li> <li>Get the temperature just cold enough for you to stand in without shivering. Over time you&rsquo;ll be able to jump in to a colder and colder shower, so know your own tolerance.</li> <li>Jump in and get wet. Only after you&rsquo;re wet all over do you turn the temperature down more, it&rsquo;s much easier that way.</li> <li>While soaping/rinsing everything, continue to turn down the temp in little increments as you adapt.</li> <li>Continue step 5 until finished washing.</li> <li>If you still have a little time left, turn the temp all the way down and stay there until you adapt to it.</li> </ol> <p>Keeping the temperature just under your comfort level promotes adaptation to the stressor, which is what we&rsquo;re going for. After a month I am noticeably much faster at adapting to extreme cold and more comfortable starting out at a much colder temperature setting.</p> <p>Eventually the goal is to be able to start on full cold and enjoy it, but it&rsquo;s much easier to work up to it than to go full blast. I&rsquo;m a big proponent of taking little steps in the right direction without going full bore and risking burn out. Habits that are painful are very difficult to start and stick with, and the protocol above makes this one easy.</p> <p>The other thing that I do is timing. I want to take a 5 minute cold shower, but just setting an alarm would be boring.</p> <p>So I just shower to music.</p> <p>I listen to &ldquo;Ain&rsquo;t It Fun&rdquo; by Paramore. The version on Spotify is 4 minutes and 57 seconds long, perfect for timing a 5 minute shower. Plus, hopefully the refrain of &lsquo;Ain&rsquo;t It Fun&rsquo; will sink in and condition me into believing I&rsquo;m having fun. The official version on YouTube is <a href="https://www.youtube.com/watch?v=EFEmTsfFL5A" class="external-link" target="_blank" rel="noopener">here</a>, but it is over a minute shorter.</p> <p>Regardless, that&rsquo;s just my choice. Go ahead and pick your own song(s) to listen to. What was very helpful for me during the first few painful showers was also having a set-up song to pick me up beforehand, and a finale song to cue me for when I finished. (I queued up &ldquo;Fuck You&rdquo; by Lily Allen to play when I was done so I could get good and mad if I wanted.)</p> <p><strong><em>A Warning</em>: I am not a doctor, nor do I play one on the internet. Talk to your doctor if you are concerned about health complications. <em>This is especially important if you have heart problems, since I do know that acute cold exposure can exacerbate existing heart conditions.</em></strong></p> <hr> <p><em>Photo by <a href="http://www.freeimages.com/photographer/pascal79-37595" class="external-link" target="_blank" rel="noopener">Pascal Montsma</a></em></p> False Memory https://jackson.dev/post/2016-02-16-false-memory/ Tue, 16 Feb 2016 00:00:00 +0000 https://jackson.dev/post/2016-02-16-false-memory/ <blockquote> <p>This is part of a series of articles examining Cognitive Biases. Go <a href="https://jackson.dev/post/2016-01-26-cognitive-biases/" >here</a> to view the rest of the series.</p> </blockquote> <p>The <strong>False Memory Bias</strong> has been on my mind recently due to an (un)fortunate binge-watching of Netflix&rsquo;s documentary series Making a Murderer. The documentary covers the trial and conviction of Steven Avery for a crime he did not commit back in 1985, his release based on DNA evidence in 2003, and his subsequent trial for the murder of Theresa Halbach in 2005. What made this documentary different from most others that cover high-profile trials is that it is not about the actual case; it&rsquo;s intended as a commentary on the legal process itself.</p> <p>Watching this documentary was a grim reminder that most portrayals of court rooms in popular media fail to understand how memories work. And it turns out, the same is true in the <em>actual legal system</em>.</p> <p>That&rsquo;s scary to me. More specifically, <em>an uneducated jury that doesn&rsquo;t understand that memories are fallible is terrifying</em>. (And prosecutors, judges, and defense attorneys that ignore the research are bad at their jobs.)</p> <h2 id="how-memory-works"> How Memory Works <a class="heading-link" href="#how-memory-works"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Let&rsquo;s step back for a minute and review how your brain actually works. When we talk about neurology it&rsquo;s important to remember that our cognition is not yet completely understood, but a lot has been fairly well-established. I will try to speak only about the generally accepted models, but the exact mechanisms for how some of these work have not been proven.</p> <p>That being said, here is the basic process by which memory works:</p> <ol> <li>You experience something.</li> <li>You store this memory in your brain. It&rsquo;s stored in multiple places.</li> <li>When you try to remember that something, your brain pulls those pieces together and <strong>reconstructs the memory from those pieces</strong>. Memory is not like a video where you just &lsquo;play it back&rsquo;, rather we collect all the associated fragments and feelings that are associated with that memory and reconstruct it on the fly.</li> <li>After recalling it, you then <strong>rewrite the memory back to your brain</strong>.</li> </ol> <p>It sounds simple and straightforward, but it causes all sorts of problems.</p> <h2 id="why-your-memory-sucks"> Why Your Memory Sucks <a class="heading-link" href="#why-your-memory-sucks"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Your memory sucks because of two separate bugs in the formula that are highlighted above. When you remember something you <strong>reconstruct it from its pieces</strong> and after recalling something you then <strong>rewrite that memory back to your brain</strong>. Memory doesn&rsquo;t work the same way as a DVD, it works more like a jigsaw puzzle; you grab a bunch of disparate pieces and fit them together in a way that seems to make sense, then put them back in their box.</p> <p>This leads to two really big problems with memory.</p> <h3 id="first-problem-you-dont-know-for-certain-if-your-memory-is-something-that-actually-happened-or-if-you-are-putting-the-pieces-together-incorrectly"> First problem, you don&rsquo;t know for certain if your memory is something that actually happened or if you are putting the pieces together incorrectly <a class="heading-link" href="#first-problem-you-dont-know-for-certain-if-your-memory-is-something-that-actually-happened-or-if-you-are-putting-the-pieces-together-incorrectly"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Numerous studies have shown that it&rsquo;s possible to create totally false memories in people. In <a href="http://users.ecs.soton.ac.uk/harnad/Papers/Py104/loftus.mem.html" class="external-link" target="_blank" rel="noopener">one well-known study</a>, the researchers were able to trick a quarter of their subjects into recalling a fake memory about being lost in a shopping mall as a child. They didn&rsquo;t even need to work very hard.</p> <p>Working with the family of their subjects, they presented them with 4 events from their childhood, one of which was the fake memory of being lost. The subjects were asked to write down what they remembered about those events and rate how confident they were with the memory being real. This was repeated a few times to see if they remembered additional details, and to further encourage their memories.</p> <p>Without going into too much detail, 25% of the subjects recalled several details about getting lost in a shopping mall when they never actually did, and several more thought they remembered getting lost but were unsure. And when asked how confident they were of it being real, <strong>some of them were very confident</strong>.</p> <p>It makes you wonder how many of your early childhood memories actually happened, and how many are there just because your parents told you about them. When I was 4 my family went on vacation, and while there I got sick and had to be taken to the hospital. For a toddler this is a fairly traumatic memory so you&rsquo;d think I&rsquo;d remember it, and you&rsquo;d be right. The only thing I recall about that trip is a distinct memory of being driven to the hospital.</p> <p>The only problem is, the streets I remember driving down are in my hometown, which is obviously wrong.</p> <p>I&rsquo;ve heard this story told to me so many times that I recall something that I <em>know</em> is wrong, that I <em>know</em> didn&rsquo;t happen like I remember, but <em>I still remember it</em>.</p> <h3 id="second-problem-its-possible-to-modify-the-memory-as-you-recall-it-and-save-it-back-again"> Second problem, it&rsquo;s possible to modify the memory as you recall it and &lsquo;save&rsquo; it back again <a class="heading-link" href="#second-problem-its-possible-to-modify-the-memory-as-you-recall-it-and-save-it-back-again"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h3> <p>Let me take an example from Making a Murderer. I&rsquo;m only going to deal with the uncontroversial false conviction of Steven Avery in 1985. The documentary covers this event in the first episode of the series, which you can watch in its entirety on YouTube.</p> <p>For those that haven&rsquo;t watched it, I&rsquo;ll summarize:</p> <p>In 1985 Penny Beerntsen was raped by a blonde haired man with a beard that bore a slight resemblance to Steven Avery, a man known to the police department at the time, but who had a very strong alibi for the time of the incident. Despite this, Steven Avery was convicted of the crime. Why? Because Penny was convinced that he did it.</p> <p>Of course it&rsquo;s not remotely her fault, her memory was effectively altered by the police&rsquo;s evidence gathering process. Here&rsquo;s the relevant sequence of events that happened after she began to give her statement:</p> <ol> <li>She describes her attacker roughly as blonde haired and with a beard, along with other descriptors.</li> <li>Someone at the Sherriff&rsquo;s office says &ldquo;That sounds like Steven Avery.&rdquo;</li> <li>Penny works with a sketch artist to produce an image of her attacker.</li> <li>The drawing produced looks exactly like an old picture of Steven Avery that the police department has on file, even though his current hairstyle (and that of the real attacker) was very different. (The filmmakers argue that the police department did this intentionally to finger Avery. For our purposes it doesn&rsquo;t matter how it happened, just that it did.)</li> <li>She picks Avery out of a line-up that includes him and other men that all appear dark haired.</li> <li>She continues to be convinced through the trial that Steven Avery was her attacker.</li> <li>18 years later, DNA evidence proved it was a different individual responsible.</li> </ol> <p>Before you start thinking that it would be really easy to confuse two men who look vaguely similar, realize that <em>the real attacker was almost a foot taller than Steven Avery</em>, who is 5'2&quot;.</p> <p>As you can likely tell by reading this cherry-picked sequence, the problem is that the police department influenced Penny&rsquo;s recollection of the event. They failed twice here in ways that reinforced each other. First, the sketch they made for her is clearly of Steven Avery. That helped solidify in her mind what her attacker looked like even though it was not entirely accurate. Whether by coincidence or not the sketch produced looks like Avery and not the person responsible. Second, they placed him in a line-up for her to pick out her attacker from. There was one man in the line-up that matched the picture the sketch artist made, and vaguely fit her description, so she was forever convinced that that was the guy.</p> <p>Think about it in terms of what we know about how memory works. Each time she was introduced to the image of Steven Avery, she was recalling the memory and trying to fit the face she saw onto her attacker. In the line-up especially, she knows that the police think they have the right person, so she picks out the person that looks close to her attacker. It&rsquo;s not her fault that she associates the image of Steven Avery with her attacker, the police as much as told her that he did it. But when she re-wrote that memory back to her brain, her modified memory now showed her that Steven Avery was the one doing it.</p> <p>Her memory alone was enough to convict despite the fact that he had 19 witnesses and several store receipts as proof that he wasn&rsquo;t there. Unfortunately the prosecutor, judge, and jury did not understand how inaccurate memories can be.</p> <h2 id="how-we-can-use-this"> How We Can Use This <a class="heading-link" href="#how-we-can-use-this"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>At first there doesn&rsquo;t seem like a whole lot of useful takeaways from knowing about this effect. You almost certainly won&rsquo;t change anything about how you see the world, but it&rsquo;s very good to be aware of. I&rsquo;m not saying don&rsquo;t believe witnesses, just keep in mind that someone can honestly be 100% certain about what they saw but also be 100% wrong. This issue comes up a lot with regards to early childhood abuse cases; it&rsquo;s very easy to accidentally &lsquo;implant&rsquo; a false memory of an early childhood event.</p> <p>One way I do know how to use this has been explored by Jane Mcgonigal, a video game researcher (sort of? I&rsquo;m not sure how best to describe her). Basically, research has shown that it&rsquo;s possible <a href="http://janemcgonigal.com/2014/03/27/help-prevent-ptsd/" class="external-link" target="_blank" rel="noopener">to use Tetris to treat PTSD</a>. I highly encourage you to follow that link and learn more, though the gist of it is this: <strong>If you experience or witness a trauma, play a pattern-matching videogame such as Tetris or Candy Crush Saga as soon as possible, <em>ideally within the first twenty-four hours after the event</em></strong>. The research shows that the visually absorbing games can prevent your brain from concentrating on the event, so your brain is prevented from storing the memories as vividly as they normally would. You can still recall the events afterwards, but you are less likely to suffer unwanted flashbacks.</p> <p>Potentially you could try it after the event as well, by recalling the memory and attempting to modify it, but that is not as effective as disrupting the storage of the memory in the first place. Interestingly, if you&rsquo;ve seen The Hunger Games movies they provide a reasonable description of doing this to one of the characters, though in the movie they do the opposite and transform someones happy memories into traumatic ones&hellip;</p> <p>Other than being aware of this while dealing with your own and others memories, I don&rsquo;t really see many everyday takeaways from knowing this even though I&rsquo;m sure there are some. Let me know in the comments what you come up with.</p> <p>(Pulling your hair out over the witness treatment in Law and Order doesn&rsquo;t count.)</p> Portion Control https://jackson.dev/post/2016-02-02-portion-control/ Tue, 02 Feb 2016 00:00:00 +0000 https://jackson.dev/post/2016-02-02-portion-control/ <p>I’m not very good with portion control. Not only in terms of my eating, but in terms of everything in my life.</p> <ul> <li>I can’t watch just 1 YouTube video, or just 1 episode of a TV show.</li> <li>I can’t play a video game for only an hour.</li> <li>I can’t drink just 1 beer.</li> <li>I can’t eat just 1 piece of chocolate.</li> </ul> <p>I don’t think I’m alone in this. Binge watching Netflix is normal for a lot of people I know, and it’s even crept into advertising campaigns.</p> <p>For me, food is the most noticeable area for catching this tendency. I know when I’m eating too much and binging on something I shouldn’t be. But other areas are more insidious.</p> <p>If I decide to spend my break time watching a YouTube video, is that really a problem? No. Not by itself at least, but for me it’s never just one. If I watch one funny video on my break, 50% of the time that turns into an hour or two of binge watching random related videos.</p> <p>The same is true in reverse, skipping out on a healthy habit is just the same as succumbing to an unhealthy activity. If you don’t do a habit 100% of the time, that actually makes it <em>more</em> difficult. It all comes down to <a href="https://jackson.dev/post/2016-01-19-willpower-is-a-limited-resource/" >willpower</a>, if you decide in advance that you’ll always do a healthy habit then you never have to waste time and willpower on the decision.</p> <p>For example, brushing and flossing your teeth. If you don’t floss once a month, that’s probably not a concern. However if you have that attitude going in, that the goal is not 100%, then you’ll inevitably miss more. Missing one isn’t a concern, but that path leads to missing more, which is a problem.</p> <h2 id="going-on-a-diet"> Going on a Diet <a class="heading-link" href="#going-on-a-diet"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>The most successful weight loss diets have a high focus on teaching portion control and adding healthy foods rather than restricting food intake. Most importantly, they place the focus on cutting out unhealthy domino foods entirely while replacing them with healthy alternatives. If the diet focuses on just restricting the amount of food you would normally eat without changing anything else, it becomes very difficult or impossible to actually follow it.</p> <p>If you control your binges then they aren’t always bad, but you need to be in control of them. That’s why <strong>planned</strong> cheat days in diets can be useful. Even though you may over eat for that 1 day, all the other days you’ve cut out binging entirely. If you aren’t honest with yourself about how much you’re actually eating though, then even allowing a cheat day in your diet or habit is a problem.</p> <p>The usual advice for dieters is to remove all of these trigger foods from your house so there is no temptation. When you get a craving it’s a lot easier to resist if you have to go all the way out to the grocery store to get it. If you’ve left it in your pantry it’s probably going to get eaten.</p> <p>The same is true for other triggers. Remove YouTube/Netflix/Facebook/etc from the favorites list on your browser. Unplug your gaming console or TV when you aren’t planning on using them. Lay out your workout clothes in the evening (or even wear them to bed!) so you don’t have to search for them in the morning.</p> <p>If you’ve got a real problem, you can go even more extreme. Several years ago I went 6 months without Internet or cable in my apartment to avoid wasting time on those useless things, and it was liberating. I watched <em>much</em> less TV, started learning how to cook, worked out consistently, was more socially active, and even started dating my current girlfriend. Interestingly I also <em>remember</em> that time more vividly. I chalk that up to being more present in everyday life, rather than being constantly focused on other things.</p> <p>Most importantly recognize these triggers in yourself. Do as much as you can to avoid the unhealthy ones at all cost, and encourage those that make you healthier.</p> <p><em>If you’re worried you may be wasting a lot of time on your computer, install <a href="https://www.rescuetime.com/" class="external-link" target="_blank" rel="noopener">RescueTime</a>. It tracks your Internet browsing and the programs you use on your computer and displays them for you in pretty graph form. Install it, including the browser plugin, and check back after a week or two to see how you do. You may find that you are spending far more time on something unproductive than you would like.</em></p> Cognitive Biases https://jackson.dev/post/2016-01-26-cognitive-biases/ Tue, 26 Jan 2016 00:00:00 +0000 https://jackson.dev/post/2016-01-26-cognitive-biases/ <p>If you’ve never been exposed to the idea of Cognitive Biases, you are probably under the impression that people are typically rational. Sure people are misinformed or uninformed or may have a different value system, but you assume that they probably make decisions consistent with the facts that they have.</p> <p><em>Unfortunately that’s just wrong.</em></p> <p>The human brain is the most complex known object in the universe, the pinnacle of billions of years of evolution, but it has some…quirks. Maybe our brains evolved too many cognitive ‘shortcuts’ to save on processing power, or perhaps the way our reasoning works still has some bugs in it, but for whatever reason humans have some issues with purely rational thought.</p> <p>While our brains are fantastic for out-competing other species (and other humans) when it comes to living on the savanna, they fall prey to what are termed Cognitive Biases. These are tendencies for the brain to think in certain ways that result in irrational behavior. Let’s look at some examples:</p> <ul> <li><a href="https://en.wikipedia.org/wiki/Illusory_truth_effect" class="external-link" target="_blank" rel="noopener">The Illusory Truth Effect</a>: Ever notice when a politician repeats false or misleading information over and over again? They’re taking advantage of this effect. When you’re exposed to information multiple times, even false information, you are more likely to believe it to be true.</li> <li><a href="https://en.wikipedia.org/wiki/Confabulation" class="external-link" target="_blank" rel="noopener">False Memories</a>: It is entirely possible for you to remember doing something that never happened, or remember something happening in a different way than it actually did. The Innocence Project, which helps overturn old legal convictions by using improved DNA testing, has reported that 73 percent of the wrongful convictions they overturned were based on eyewitness testimony. In addition, the Columbia Human Rights Law Review published an investigation in May 2012 reporting that Texas almost certainly executed an innocent man, <a href="https://en.wikipedia.org/wiki/Carlos_DeLuna" class="external-link" target="_blank" rel="noopener">Carlos DeLuna</a>, based on eyewitness testimony. <a href="http://www.scientificamerican.com/article/do-the-eyes-have-it/" class="external-link" target="_blank" rel="noopener">Read this</a> and never fully trust an eyewitness again.</li> <li><a href="https://en.wikipedia.org/wiki/Hindsight_bias" class="external-link" target="_blank" rel="noopener">Hindsight Bias</a>: This is when we believe that past events were easily predictable at the time. While the chain of events looks easy to see in hindsight, we overestimate how easy it would have been to predict in the past. The 2008 financial crash looks like it should have been obvious to predict, but we have the advantage of knowing what happened, and few people caught it at the time. Falling prey to their Hindsight Bias is why a lot of people think they can speculate on the stock market. The reality is hindsight really isn’t 20/20.</li> <li><a href="https://en.wikipedia.org/wiki/Bluma_Zeigarnik#The_Zeigarnik_effect" class="external-link" target="_blank" rel="noopener">The Zeigarnik Effect</a>: Incomplete tasks are better remembered than complete tasks. If you’re finding yourself in the evenings stuck in a mental loop remembering your work day, chances are you’re trying to keep in mind details of an unfinished task or project. Unload all those ideas at the end of the work day and enjoy your evening.</li> </ul> <p>To be clear, that is nowhere close to an exhaustive list. <a href="http://biaslist.com/" class="external-link" target="_blank" rel="noopener">This handy website</a> lists many of them if you’d like to browse through others yourself.</p> <h2 id="why-you-should-care"> Why You Should Care <a class="heading-link" href="#why-you-should-care"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>There are two big reasons why studying Cognitive Biases and learning how to manage them are so important.</p> <p><strong>First, it’s great internal introspection.</strong> If you are really focused on improving yourself, knowing your own faults is vital. Understanding when and how you will make irrational decisions is the first step in becoming a rational thinker. Increasing your own rationality improves your decision making process, and thus every decision you make.</p> <p><strong>Second, you can use it for external action.</strong> Use these deficiencies in rational thinking to manipulate the world to suit your goals. Take advantage of these faults of logic in others and get your competitors to act irrationally. But please don’t use them to take over the world, that would be bad.</p> <p>In future articles I’ll cover some of these biases in greater detail and hopefully discuss some strategies for dealing with them in your own life.</p> Willpower is a Limited Resource https://jackson.dev/post/2016-01-19-willpower-is-a-limited-resource/ Tue, 19 Jan 2016 00:00:00 +0000 https://jackson.dev/post/2016-01-19-willpower-is-a-limited-resource/ <p><a href="http://faculty.washington.edu/jdb/345/345%20Articles/Baumeister%20et%20al.%20%281998%29.pdf" class="external-link" target="_blank" rel="noopener">In a landmark 1998 study</a>, 67 people were tested to try to determine if willpower was a limited resource. The scientists were able to show that they could deplete willpower in their test subjects through a very simple mechanism, chocolates and radishes.</p> <p>The experimenters setup a waiting room with a tray of snacks; half the tray filled with radishes and the other with chocolates. One group of participants was instructed to wait in the room and eat the chocolates, while the other group was made to wait in the same room but only allowed to eat the radishes (a third group was used as a controls and wasn’t presented with either food). After they waited and snacked in this room, each of the participants was tasked with completing an impossible geometry puzzle. The experimenters were measuring how long it took each participant to give up on trying to solve it, and the results confirmed their suspicions.</p> <p>The chocolate group spent 19 minutes trying to solve the puzzle before quitting (The control group gave up after 20 minutes, statistically the same as the chocolate group). The second group, the one tasked with eating radishes and ignoring the chocolate, worked for only 8 minutes before giving up. The radish group gave up more than twice as fast as the chocolate group. The theory of why this happened comes down to what the experimenters term ‘ego depletion’, that the subjects eating the radishes used up their willpower reserves and thus had less willpower to use in the puzzle test. In the years since, this result has been confirmed again and again in related studies.</p> <p>Let me step back a moment and define the terms. I used willpower in the title of this article as that is the most recognizable term to describe the effect, however the technical term is ‘ego depletion’. Roy Baumeister, perhaps the leading researcher on the subject and one of the authors of the radishes experiment, defines it this way: “The core idea behind ego depletion is that the self’s acts of volition draw on some limited resource, akin to strength or energy and that, therefore, one act of volition will have a detrimental impact on subsequent volition.” In short, willpower is finite.</p> <p>The studies show that this same quantity is linked to everything you would normally think of as self-control. Namely:</p> <ul> <li>Focus and Concentration</li> <li>Decision making</li> <li>Suppressing emotions</li> <li>Pushing yourself during a workout</li> <li>Etc.</li> </ul> <p>It’s a measurable physical effect as well. In one experiment, subjects that underwent willpower depletion were shown to have lower glucose levels than subjects who did not. And similarly, feeding ego depleted subjects a sugar-sweetened drink seemed to restore some of their willpower (compared to giving them a drink with artificial sweetener). Glucose, a simple sugar used by your body for fuel, is the primary fuel source used by the brain.</p> <h2 id="why-you-should-care"> <strong>Why You Should Care</strong> <a class="heading-link" href="#why-you-should-care"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>Sure it’s an interesting experimental result, but what’s really great about the theories of ego depletion are their applicability for personal productivity.</p> <p>In his book, <a href="https://en.wikipedia.org/wiki/Daily_Rituals:_How_Artists_Work" class="external-link" target="_blank" rel="noopener">Daily Rituals</a>, Mason Currey describes the daily rituals of highly creative people. Overwhelmingly, he reports that most of them maintained a set of routines and rituals each day, keeping every day relatively similar. As a recent example, Steve Jobs famously wore the same clothes every day, since while he couldn’t control his daily schedule he could control what he wore each day.</p> <p>The key point here is not necessarily what any of the rituals actually are, but the fact that they exist at all. What matters is not that Steve Jobs wore the same thing every day, or that Charles Darwin went for a walk every day at noon, but that they did not have to waste their willpower on deciding what to do or wear. Instead, creatives with a ritual are free to use all of their available willpower on the pursuits that they care about.</p> <p>The studies also show that willpower can be restored via food, sleep, and time. So for many people the morning may be the best time to do your most focus intensive work; that’s when your willpower stores are at their highest.</p> <h2 id="apply-the-science"> <strong>Apply the Science</strong> <a class="heading-link" href="#apply-the-science"> <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i> <span class="sr-only">Link to heading</span> </a> </h2> <p>There are a lot of useful nuggets we can draw from this concept. Most important is to simply stay aware of this fact while planning your schedule. Try scheduling your intense work early in the day. Avoid decision making wherever possible on the least useful parts of your day.</p> <p>Perhaps most importantly, instead of looking at tomorrow’s empty schedule by saying ‘I’ll figure out what to do in the morning’, rephrase that to be ‘I’m going to use up my limited focus tomorrow morning by checking email and deciding what to work on.’ If you’re still comfortable with that, by all means go ahead.</p> <p><em><strong>Further Reading</strong>: Roy Baumeister co-wrote an excellent book that explores this in depth. <a href="https://wikipedia.org/wiki/Willpower:_Rediscovering_the_Greatest_Human_Strength" class="external-link" target="_blank" rel="noopener">Willpower: The Greatest Human Strength</a></em></p> Resolutions https://jackson.dev/post/2016-01-12-resolutions/ Tue, 12 Jan 2016 00:00:00 +0000 https://jackson.dev/post/2016-01-12-resolutions/ <p>There’s something about this time of year that inspires everyone to reflect on their accomplishments and look towards the future. There’s nothing inherently special about January 1st, it marks nothing of note in the natural world; the only thing significant is that the last digit of the date changes.</p> <p>Still, it’s a time for many to make their New Years Resolutions. Almost everyone makes them it seems, though many are too embarrassed to admit them out loud. Everyone is dissatisfied with something about their life, and the constant Buzzfeed-style articles about Resolutions ensure that they are top of mind for most.</p> <p>The problem is most people suck at making real, lasting changes in their life.</p> <p>Let’s take an example: Jane</p> <blockquote> <p>Jane is 29, and she’s unhappy with how she looks. She’s been working a desk job for several years and her body is starting to show it. She’s overweight and dreams of one day having her bikini-ready body back. So she decides to make a resolution for New Years; she’s going to get in shape. Jane buys a year long gym membership and promises herself that she’ll go 3 days a week for the entire year.</p> <p>For the first couple weeks, things go well. Jane dutifully drags herself into the gym 3 days a week, fighting the massive January gym crowds to spend the requisite 30 minutes working out on the elliptical machine. But one week, at the end of January, she has to work overtime and misses a few days.</p> <p>Over the course of the next couple days, she doesn’t go either. She’s too tired from working, or deserves some time off exercising as a reward, she tells herself. A few days off turns into a week, then two weeks, and on and on. Jane never gets back on schedule.</p> <p>She tries again when her 30th birthday rolls around in August, since she still has that gym membership, and lasts a full month before quitting again.</p> <p>At the end of the year, Jane feels bad about her body and decides that the next year she’ll do better. So she buys a gym membership, resolves to go 3 days a week, and starts fighting the January gym crowds to work out on the elliptical and…</p> </blockquote> <p>Well that probably won’t work, but you already knew that.</p> <p>Unfortunately this is how people seem to think they should go about pursuing goals, and it’s why we’re so bad at completing them. It doesn’t mean that you lack willpower or fortitude or resolve or whatever else, it just means that you might be going about it the wrong way. I’m not saying that what Jane did won’t work for some people, but it surely does not work for the vast majority.</p> <p>Jane made a number of mistakes in trying to lose weight:</p> <ul> <li>She didn’t specify a goal weight. She used ‘get in shape’ as her goal, which isn’t measurable. There isn’t a way to track progress towards ‘getting in shape’ and she can’t really say when she’s done. Without a clear goal in mind it’s difficult to measure how you’re progressing.</li> <li>She focused on the wrong things. If we assume her goal was to lose some excess weight, working out is not the most important thing for her to be doing. The most important factor for weight loss is diet, so her focus should have been to work on that at first. (As the eloquent folks over at Nerd Fitness have put it, <a href="http://www.nerdfitness.com/blog/2009/07/15/you-cant-outrun-your-fork/" class="external-link" target="_blank" rel="noopener">“You can’t outrun your fork”</a>)</li> <li>She didn’t schedule (or reschedule) her workouts. If you have to decide day by day what you’re going to do, you’ll eventually have an ‘off’ day or three and miss the necessary work.</li> <li>She lacked an accountability partner. Jane, we can assume, didn’t tell anyone else what her goals were. She didn’t have someone she was accountable to that could help push her back on track when she faltered.</li> <li>She tries multiple times to change without adjusting any of her plans, hoping that this time she’ll have the willpower to do it. She doesn’t learn from her mistakes and will get more and more frustrated as time goes on.</li> </ul> <p>Here’s the formula that I use to accomplish actionable goals. It’s not particularly detailed, but that’s the point. Everybody is different in what will push them to get what they want, and the idea is to adjust and modify your process for working towards your goals to get there as effective and efficiently as possible.</p> <ol> <li><strong>Pick a measurable, specific goal.</strong> You want to be able to say definitively that you accomplished what you set out to do. For weight loss, this means having a goal weight or, even better, a goal body fat percentage. If the deadline is very far away it can be difficult to gauge what you will be able to accomplish, so be willing to adjust if necessary. (As an example, I once accomplished a New Years Resolution before the end of January, so clearly I was underestimating what was possible. Adjust the goal and move on.)</li> <li><strong>Figure out how to accomplish it.</strong> Depending on the goal the path forward may be obvious, but often it isn’t. In the example above, Jane should have started by adjusting her diet first, but she started with working out because she didn’t know the most efficient way to accomplish her goal. Do a little research now and save yourself lots of unnecessary work later.</li> <li><strong>Break it down into actionable steps or checkpoints</strong>, all the way down to what you need to do today or this week. This way you can gauge your progress, plan your work, and know when or if you need to adjust your end goals.</li> <li><strong>Schedule it</strong>. Willpower is a limited resource, so don’t just hope you’ll be in the mood later to follow through, schedule it in advance. If applicable, set aside time in your calendar beforehand to work on it.</li> <li><strong>Get an accountability partner.</strong> Tell at least one other person what you’re doing, and make sure they will hold you to it. If you want to really stay on track, get them to agree to punish you in some way if you don’t stick to it (Studies show that people respond better to negative rather than positive reinforcement). Perhaps give them $50 to hold and if you don’t stay on track, they’ll donate it to a cause you hate. Close friends or family are best, and strangers on the internet are a last resort. I will readily admit that this step is the hardest for me personally, and I struggled with doing this for a long time, but being accountable to another real person is a big motivator to keep going.</li> <li><strong>Regularly review and update.</strong> Pick a day once a week or once a month, to review how you’re doing with the goal. Check in with your accountability partner, adjust the goal if necessary, plan the next week or month, etc. I have a set weekly review time each week for all of my goals and plans.</li> </ol> <p>Let me know how this works out for you in the comments, or what else you’ve found that works for you personally, I’m always interested in hearing about it.</p>