<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>shazow.net | Andrey Petrov</title><link>https://shazow.net/</link><description>Recent content on shazow.net | Andrey Petrov</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Thu, 02 Apr 2026 13:22:44 -0400</lastBuildDate><atom:link href="https://shazow.net/index.xml" rel="self" type="application/rss+xml"/><item><title>Permissive vs Copyleft Open Source</title><link>https://shazow.net/posts/permissive-vs-copyleft/</link><pubDate>Wed, 09 Jul 2025 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/permissive-vs-copyleft/</guid><description>&lt;p>The premise of copyleft licenses is attractive: Create more open source!&lt;/p>
&lt;p>With permissive licenses, someone can take the code and make proprietary modifications to it and sell it to other people without releasing the modifications. We want people to publish their improvements, right?&lt;/p>
&lt;p>With copyleft, we can force people to publish their improvements to copyleft code. Businesses will want to use our code because creating it was so much work in the first place. We need copyleft if we want more contributors, more open source, more code re-use, more freedom.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>The premise of copyleft licenses is attractive: Create more open source!</p>
<p>With permissive licenses, someone can take the code and make proprietary modifications to it and sell it to other people without releasing the modifications. We want people to publish their improvements, right?</p>
<p>With copyleft, we can force people to publish their improvements to copyleft code. Businesses will want to use our code because creating it was so much work in the first place. We need copyleft if we want more contributors, more open source, more code re-use, more freedom.</p>
<p>Right?</p>
<p>Wrong.</p>
<p>In this post, I break down all the ways copyleft licenses fail to achieve their stated goals, and explain why permissive licenses succeed where copyleft fails.</p>
<h2 id="background">Background</h2>
<p>(Skip this if you already know the difference between permissive and copyleft licenses.)</p>
<p>Open source licenses were created for important reasons: To protect the author, to protect the contributors, and to protect the consumers. This is achieved through codifying rights regarding redistribution, derived works, discrimination, and disclaimer of liability/warranty. Perhaps most importantly, an open source license is designed around allowing us to &ldquo;fork&rdquo; a project while retaining all the rights in the license.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>When picking an open source license, there&rsquo;s two camps: Permissive licenses (like BSD, MIT, Apache-2.0) and copyleft licenses (like MPL, GPL, AGPL).</p>
<p>Permissive licenses give the author the same rights as the contributors and consumers. Everyone can do anything as long as the license is not removed from where it is. If you want to use permissively-licensed code in a proprietary program, go wild.</p>
<p>Copyleft licenses are different&ndash;they were created as an exploit of our intellectual property and copyright system, a kind of &ldquo;virus&rdquo; to combat our proprietary rights-reserved-by-default legal system. With copyleft licenses, derivative work is required to perpetuate the same license. The boundary of derivation varies between licenses. For example: MPL constrains at the &ldquo;file&rdquo; level, GPL is at the binary distribution level which can be bypassed with dynamic linking, AGPL is at the &ldquo;network&rdquo; level specifically to target SaaS usage.</p>
<p>There are many downstream repercussions of these details that I&rsquo;ll discuss below.</p>
<h2 id="copyleft-is-asymmetric">Copyleft is asymmetric</h2>
<p>(Please bear with me as I take a brief detour to explain this not-very-intuitive point that comes up again and again. It turns out the asymmetry of copyleft is a critical flaw relevant in many scenarios.)</p>
<p>In the construct of our society&rsquo;s intellectual property law<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, the author retains copyright by default. If I write some code and don&rsquo;t explicitly release my rights to it, then it is considered proprietary and owned by me by default. <em><a href="https://www.copyright.gov/help/faq/faq-general.html">I don&rsquo;t even need to write &ldquo;all rights reserved&rdquo;!</a></em></p>
<p>Having copyright over my code gives me the right to license it however I want. I can keep it proprietary, or I can make a custom contract to sell to a customer or another publisher, or I can release it under a common license template like an open source license.</p>
<p>When I commit code to a code repository with some open source license, I am consenting to include my code under the project license (which usually lives in the root of the repository and applies to the whole thing unless said otherwise). <strong>But because I still own the copyright to my work, I can <em>also</em> release the same code under a different license in a different repository.</strong></p>
<p>This means as an author of some copyleft code, I have special rights that my users don&rsquo;t have: I am allowed to use my code for proprietary purposes, but my users are not.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>Permissive licenses don&rsquo;t have this asymmetry by virtue of giving everyone the right to use the code for proprietary purposes.</p>
<p>Okay, why is this important?</p>
<h2 id="custom-licensed-copyleft-code-is-a-liability">Custom-licensed copyleft code is a liability</h2>
<p>Some projects choose copyleft licenses as a way to convince businesses to pay for a custom license.</p>
<p>On paper, this sounds rational: The code is available to review and even prototype against, but deploying to customers may cross the viral copyleft boundary and a business would choose to pay for a custom license from the maintainer to avoid relicensing their entire product.</p>
<p>For every dependency that a business pays for a custom license, it adopts a liability and creates another point of failure. Each dependency has the power to rug the business: They can choose not to renew the license next year, or they can choose to pivot the project in an incompatible direction, or they can refactor in a way that increases the copyleft boundary.</p>
<p>Most importantly, the business has no recourse!<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup></p>
<p>Recall that with permissive licenses, we all have the same rights: If a maintainer does something that doesn&rsquo;t work for me, I can simply fork and continue using the code as I was before. I don&rsquo;t lose any rights even if I was depending on a custom license before.</p>
<p><strong>It is safer for a business to do a contract deal with a permissively licensed project!</strong></p>
<p>With copyleft and a custom license, I don&rsquo;t have the protection of retaining my rights if something goes awry. If my business model or infrastructure architecture is incompatible with the copyleft nature of the dependency, then my entire business is at risk.</p>
<p>Now imagine how much worse this could be at scale: Naively, it seems like a great idea &ldquo;if all dependencies were copyleft and we automated licensing them&rdquo; then these liabilities would compound exponentially.</p>
<h2 id="code-is-not-a-whole">Code is not a whole</h2>
<p>There is a common assumption from copyleft-licensed projects that the consumer will use the project as a whole. This is incorrect on two fronts:</p>
<ol>
<li>It&rsquo;s very common for programmers to get stuck on a problem, see how another project solves it, and adopt their solution if the license permits. In many cases, this can be as small as a 20 line function that has a clever optimization. This is how much progress is made in software, rather than always reinventing the wheel poorly.</li>
<li>When a consumer uses a project wholesale, they&rsquo;re not depending on 100% of the code in the repository. For example, I may only be using 20% of the features a project implements, and I may never run the unit tests, or validate the formal verification, or access the modding interfaces, etc. <strong>This is important because it represents the &ldquo;replacement value&rdquo; of a project to the consumer</strong>, which we&rsquo;ll discuss next.</li>
</ol>
<h2 id="relicense-or-rewrite">Relicense or rewrite?</h2>
<p>The stated goal of copyleft is to create more copyleft source code, but in practice it does the opposite: <strong>Copyleft creates more proprietary code.</strong></p>
<p>When a business finds a dependency that they&rsquo;d like to use, but notice that it&rsquo;s copyleft-licensed, the business has three choices:</p>
<ol>
<li>Open source and relicense all internal code that would be impacted by integrating this dependency.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></li>
<li>Rewrite the dependency inhouse. (Or find another permissively licensed substitute, if such exists.)</li>
<li>Request a custom license from the copyright holders, see: <a href="#custom-licensed-copyleft-code-is-a-liability">Custom-licensed copyleft code is a liability</a>. (This is also logistically difficult if the project has many contributors without a consolidated copyright holder, as each contributor would need to consent.)</li>
</ol>
<p>I&rsquo;ve been working as an open source maintainer and contributor 20+ years and not a single time in my life have I seen a business willingly<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup> make public and relicense their internal code to comply with a dependency they were considering using. Not once!</p>
<p>On the other hand, I&rsquo;ve personally witnessed <em>many</em> cases where a business would do a clean room rewrite of copyleft-encumbered code to avoid touching it. In fact, I&rsquo;ve done work like this myself on several occasions and have been lucky to convince my employers to allow me to do it under a permissive MIT license. By default, rewrites stay locked up in an internal repository that no one else gets to benefit from.<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup></p>
<p>Why rewrite the code? <strong>Because it&rsquo;s wildly easier to rewrite code after the design boundaries have been established.</strong></p>
<p>If you have a mature codebase that your team spent a person-year building, ask them: &ldquo;Given everything we&rsquo;ve learned, how long would it take for us to rewrite this in a different language?&rdquo; The answer to this is often around 10× faster. Much of the time spent building the first version is finding and avoiding all of the &ldquo;wrong decisions&rdquo;, iterating with user feedback, reframing features to make more holistic sense, testing them, polishing them, arguing over button colours, etc.<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup></p>
<p>Second, consider the &ldquo;replacement value&rdquo; of the project (from the previous section). If we only need 20% of the functionality, we don&rsquo;t need to rewrite 100% of the code.</p>
<p>If you spent 52 weeks building 100% of the functionality and supporting infrastructure, and I just need 20% of it that I can rewrite 10× faster&hellip; that&rsquo;s just a couple weeks of work!<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup></p>
<p>Why would a business pay for an existential liability that when they could just have a half-decent programmer do an in-house rewrite of the relevant component in two weeks?</p>
<h2 id="permissive-licenses-create-more-open-source">Permissive licenses create more open source</h2>
<p>While copyleft claims (but fails) to create more open source code, <em>permissive</em> code actually achieves this by default.</p>
<p>When a business uses permissively licensed code and runs into a bug or needs to patch an improvement, they have two choices:</p>
<ol>
<li>Maintain an internal downstream fork with modifications, dealing with rebase conflicts with every update.</li>
<li>Send a pull request upstream with the modifications, and have the community maintain it for free.</li>
</ol>
<p>No one wants to maintain and rebase an internal fork, it&rsquo;s frustrating and grueling work with negligible benefits unless this one modification is the very core differentiator of your business&ndash;and this is extremely rare, especially considering most software businesses have hundreds or thousands of dependencies.</p>
<p>By default, businesses tend to contribute more to permissive open source code in their own self-interest.</p>
<h2 id="copyleft-reduces-the-contributor-pool">Copyleft reduces the contributor pool</h2>
<p>Most software businesses outright ban their employees from touching strong copyleft licenses like AGPL. For example, <a href="https://opensource.google/documentation/reference/thirdparty/licenses#banned">Google&rsquo;s document on allowable licenses for third party dependencies</a>.<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup></p>
<p>Some proponents of copyleft claim that this is a feature, not a bug: Why should we benefit businesses at all, when we could only benefit individuals?</p>
<p>Unfortunately, this exclusion is counterproductive because nearly every programmer in our capitalist society is employed by such a business. At minimum, these projects miss out from people who would upstream fixes and improvements during work hours, but also many employment agreements have much influence over what people do in their personal time (if not practically then at least emotionally).</p>
<p>And it&rsquo;s not just businesses!</p>
<p>I am an independent open source developer who is passionate about permissively licensed code (could you have guessed?) and I create a lot of it. <strong>I will not even read AGPL code</strong> because I don&rsquo;t want to risk possibly contaminating some of my many other projects.</p>
<h2 id="copyleft-nuances-and-complexity">Copyleft nuances and complexity</h2>
<p>I can write Python that has a copyleft GPL dependency without relicensing my project to match, but I can&rsquo;t write Go that has a GPL dependency without relicensing. Why?</p>
<p>Python is interpreted and the code is linked dynamically, whereas Go produces a statically linked binary so the copyleft &ldquo;infects&rdquo; the rest of the code.</p>
<p>Unless, of course, if I want to ship my Python program as a py2exe bundle with a GPL dependency, then that changes everything.</p>
<p>Does anyone actually understand these implications and how they vary across languages? Of course not!</p>
<p>But wait, what if I split out the GPL dependency into a shared library that is dynamically linked? Oh, that&rsquo;s fine. Unless you ask the FSF, who disagrees with almost everyone else.</p>
<p>What about other copyleft licenses like LGPL? MPL? AGPL?</p>
<p>What if I wrap an AGPL dependency in a network-isolated container which batch-processes input from a proprietary component in my system? That&rsquo;s fine.</p>
<p>Did we really improve anything or are we just asking people to create complex infrastructure and deployment workarounds?</p>
<p>I liked this quote from <a href="https://lobste.rs/s/ezfkac/why_we_picked_agpl#c_7436dr">David Chisnall on lobste.rs</a> (the rest is worth reading too):</p>
<blockquote>
<p>[&hellip;] And this is reason #3742648 why I don’t contribute to AGPL things: They place a compliance burden on good-faith actors that are trivial to bypass for bad-faith actors.</p>
</blockquote>
<h2 id="permissive-licenses-create-public-goods">Permissive licenses create public goods</h2>
<p>While copyleft licenses have several exclusionary clauses (e.g. can&rsquo;t use this code if you&rsquo;re statically linking against other code that has a different license), permissive licenses do not. Copyleft licenses give special rights to the author, permissive licenses do not. Strong copyleft licenses are practically banned from large subsets of consumers and contributors (we&rsquo;ll debate if this is justifiable), permissive licenses are not.</p>
<p>Permissively licensed code is a better fit under the &ldquo;both non-excludable and non-rivalrous&rdquo; definition of public goods.</p>
<h2 id="copyleft-fails-to-prevent-corporate-capture">Copyleft fails to prevent corporate capture</h2>
<p>A common complaint is that Amazon AWS exploits open source by profiting from it without sufficiently contributing back, and that the only solution is strong copyleft like AGPL.</p>
<p>Until 2018, MongoDB was copyleft AGPL licensed, and Amazon AWS happily provided a hosted service for MongoDB. MongoDB didn&rsquo;t like that Amazon was profiting from their work, so on October 2018, MongoDB changed their license to a commercial source-available license called SSPL&ndash;specifically to exclude Amazon being able to use it this way. By January 2019, just 2.5 months later, Amazon built and released a <strong>proprietary</strong> API-compatible version called DocumentDB.</p>
<p>MongoDB was being developed since 2009, for 9 years. Did it take Amazon 9 years to rewrite the &ldquo;replacement value&rdquo; of their service? No, it took 2.5 months.</p>
<p>Did AGPL save MongoDB? No.</p>
<p><strong>Did copyleft AGPL create more open source code?</strong> No, Amazon did a proprietary internal full rewrite and never published the code as open source.</p>
<p>Did relicensing to an even more restrictive license force Amazon to give MongoDB more money? No.</p>
<p>This has happened again and again. Elasticsearch relicensed from permissive Apache-2.0 to commercial SSPL in 2021, which resulted in the previous Apache-2.0 version being forked and maintained as OpenSearch. Amazon contributed code to the permissively licensed version, but Elasticsearch did not succeed at extracting more money from Amazon by using a more restrictive license. In 2024, Elasticsearch changed their mind and <a href="https://www.elastic.co/blog/elasticsearch-is-open-source-again">relicensed to copyleft AGPL</a>, but AWS continues to use and contribute to OpenSearch.</p>
<p><strong>Did permissive Apache-2.0 create more open source code? YES!</strong> Unlike the copyleft AGPL example, Amazon forked the permissively-licensed project and continued to maintain it in public, allowing everyone else to benefit too.</p>
<p>In 2024, Redis (permissively licensed under BSD) relicensed to a Source Available license, same story: Redis failed to extract more money from customers, instead a permissive community fork continued under Valkey. In 2025, Redis changed their mind and <a href="https://redis.io/blog/agplv3/">relicensed to copyleft AGPL</a>! What happened? It&rsquo;s still early, but so far Valkey (BSD) continues to thrive, Redis continues to stagnate.</p>
<h2 id="how-do-we-make-businesses-contribute-to-open-source">How do we make businesses contribute to open source?</h2>
<p>I get this question a lot, and my general answer is: They already do, just not exactly in the way we might want.</p>
<p>When we imagine businesses contributing to open source, we imagine them sending money to maintainers of dependencies they use. While this does happen, it&rsquo;s very rare.</p>
<p>What&rsquo;s less rare is businesses increasingly choose to release parts of their code as open source code, and contribute improvements to other projects. While this doesn&rsquo;t help overburdened maintainers, it is otherwise a good thing!</p>
<p>I believe the lowest hanging fruit is to encourage businesses to get <em>more</em> involved with creating and maintaining open source code by empowering their employees to do so.</p>
<p>The best way for maintainers to grow funding from businesses who use their software is to establish relationship with their &ldquo;customers&rdquo; and find services that are valuable for these customers. For example, funding to work on features that are important for the customers, or funding for an annual support retainer.</p>
<h2 id="what-license-should-i-use">What license should I use?</h2>
<p>If you&rsquo;re optimizing for adoption and impact, I suggest the most permissive license that is popular in your community. Usually that&rsquo;s MIT or Apache-2.0.</p>
<p>If you&rsquo;re trying to create a self-sustaining silo of code that doesn&rsquo;t get reused outside of a comparatively small ecosystem, then copyleft licenses achieve this.</p>
<p>If you&rsquo;re trying to exclude groups from using your code, like for-profit businesses or the military industrial complex or people with disagreeable political alignments, then open source is probably not for you because that would violate the <a href="https://opensource.org/osd">Open Source Definition</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In the past couple of decades, permissive licenses (like BSD, MIT, Apache-2.0) have succeeded at creating more open source code, more collaboration and mutual participation from businesses, more code-reuse, and more public goods.</p>
<p>This wasn&rsquo;t always true: In the 90s, copyleft was king. Businesses were very skeptical of open source and security through obscurity was a popular mentality. In many ways, businesses considered themselves at war with open source. <a href="https://web.archive.org/web/20011211130654/http://www.suntimes.com/output/tech/cst-fin-micro01.html">Recall this 2001 interview quote from Steve Ballmer (Microsoft CEO at the time)</a>:</p>
<blockquote>
<p>Q: Do you view Linux and the open-source movement as a threat to Microsoft?</p>
<p>A: Yeah. It&rsquo;s good competition. It will force us to be innovative. It will force us to justify the prices and value that we deliver. And that&rsquo;s only healthy. The only thing we have a problem with is when the government funds open-source work. Government funding should be for work that is available to everybody. <strong>Open source is not available to commercial companies. The way the license is written, if you use any open-source software, you have to make the rest of your software open source. If the government wants to put something in the public domain, it should. Linux is not in the public domain. Linux is a cancer that attaches itself in an intellectual property sense to everything it touches. That&rsquo;s the way that the license works.</strong></p>
</blockquote>
<p>Of course this was a misinformed and naive view, but the seed of the concern was real as discussed in the <a href="#custom-licensed-copyleft-code-is-a-liability">Custom-licensed copyleft code is a liability</a> section.</p>
<p>In 2025, the landscape is very different. Businesses have found a mutually-aligned interest with permissive open source, and the ecosystem has exploded thanks to that.</p>
<p>Is this the best we can do? I hope not!</p>
<p>Let&rsquo;s continue to find more <a href="https://en.wikipedia.org/wiki/Focal_point_(game_theory)">Schelling points</a> of collaboration and aligned interests, so that the public can benefit from a larger portion of private effort.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The Open Source Initiative maintains <a href="https://opensource.org/osd">The Open Source Definition</a> and <a href="https://opensource.org/licenses">the sets of licenses which comply with it</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Our intellectual property construct goes back all the way to the <a href="https://en.wikipedia.org/wiki/Berne_Convention">Berne Convention</a>, first adopted in 1886. Today, 181 countries have ratified it.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>The special right of the copyright holder is made even more dangerous when a project requires an IP assignment agreement (also known as a Contributor License Agreement). These agreements assign all the copyright to a single owner (often the maintainer or a holding entity), so that they can unilaterally relicense the work without requiring individual consent from every contributor.</p>
<p>OSS licenses were designed to leverage the logistic hurdle of requiring consent of all contributors.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>We can imagine a very specific contract that outlines all the ways the project codebase must continue to be compatible with the business&rsquo;s needs for a long duration, but is this actually plausible? We don&rsquo;t know what we don&rsquo;t know, both businesses and open source projects continually evolve. Plus, it&rsquo;s incredibly hard to negotiate bespoke agreements between every combinatorial pair of business and dependency.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>I cannot understate how expensive it is to take a private legacy codebase and make it public. When a codebase is private, every choice is made with that context: Secrets are littered in the commit history, assumptions are hardcoded, proprietary business details are exposed.</p>
<p>The longer we wait, the more expensive it gets. All of those micro-decisions add up, sometimes to the point where it&rsquo;s cheaper to just rewrite the whole thing in public from scratch.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>There were several cases where businesses &ldquo;accidentally&rdquo; used copyleft code and were later forced to relicense parts of their proprietary code to comply retroactively, but this was a mistake insofar that they would not have made this choice if they knew of the impending outcome. For example: <a href="https://en.wikipedia.org/wiki/Free_Software_Foundation,_Inc._v._Cisco_Systems,_Inc.">Linksys</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p>Example: When Amazon AWS rewrote their MongoDB (AGPL) service as DocumentDB (internal/proprietary). Discussed more below in <a href="#copyleft-fails-to-prevent-corporate-capture">Copyleft fails to prevent corporate capture</a>.&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:8">
<p>It was 10× faster before AI-assisted tooling, who knows how much faster it will be in the future! It&rsquo;s certainly not getting slower. Again, AWS DocumentDB was created as a replacement for MongoDB in just 2.5 months, and this was in 2019.&#160;<a href="#fnref:8" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:9">
<p>Yes these numbers are made up and sound a little wild, but they&rsquo;re closer to the truth in the vast majority of cases than is comfortable to admit. There are of course examples of copyleft projects like the Linux kernel which contain tens of thousands of person-years of effort, such that even a 100× improvement is still very costly&ndash;perhaps this was a lesson that Google Fuchsia learned.&#160;<a href="#fnref:9" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:10">
<p>Apple&rsquo;s App Store prohibits apps which include copyleft licensed code <a href="https://www.fsf.org/blogs/licensing/more-about-the-app-store-gpl-enforcement">due to EULA contention pointed out by the FSF</a>. This restricts potential contributors who are building mobile apps.&#160;<a href="#fnref:10" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

      ]]></content:encoded></item><item><title>urllib3 Origin Story</title><link>https://shazow.net/posts/urllib3/</link><pubDate>Wed, 21 May 2025 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/urllib3/</guid><description>&lt;div class="notice note">
 &lt;strong>Syndicated&lt;/strong>
 &lt;p>This post was featured on &lt;a href="https://opensource.org/maintainers/shazow">opensource.org/maintainers/shazow&lt;/a>&lt;/p>
&lt;/div>

&lt;p>My first big open source project was &lt;a href="https://github.com/urllib3/urllib3/">urllib3&lt;/a>. Today it’s used by almost every Python user and receives about a billion downloads each month, but it started in 2007 out of necessity.&lt;/p>
&lt;p>I was working at &lt;a href="https://tineye.com/">TinEye&lt;/a> (formerly known as Idée Inc.) as my first “real” job out of university, and we needed to upload billions of images to Amazon S3. I wrote a script to get processing and estimated how long it would take to finish… two months! Turns out in 2007, HTTP libraries weren’t reusing sockets or connection pooling, weren’t thread-safe, didn’t fully support multipart encoding, didn’t know about resuming or retries or redirecting, and much more that we take for granted today.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <div class="notice note">
    <strong>Syndicated</strong>
    <p>This post was featured on <a href="https://opensource.org/maintainers/shazow">opensource.org/maintainers/shazow</a></p>
</div>

<p>My first big open source project was <a href="https://github.com/urllib3/urllib3/">urllib3</a>. Today it’s used by almost every Python user and receives about a billion downloads each month, but it started in 2007 out of necessity.</p>
<p>I was working at <a href="https://tineye.com/">TinEye</a> (formerly known as Idée Inc.) as my first “real” job out of university, and we needed to upload billions of images to Amazon S3. I wrote a script to get processing and estimated how long it would take to finish… two months! Turns out in 2007, HTTP libraries weren’t reusing sockets or connection pooling, weren’t thread-safe, didn’t fully support multipart encoding, didn’t know about resuming or retries or redirecting, and much more that we take for granted today.</p>
<p>It took me about a week to write the first version of what ultimately became urllib3, along with workerpool for managing concurrent jobs in Python, and roughly one more week to do the entire S3 upload using these new tools. A month and a half ahead of schedule, and we became one of Amazon’s biggest S3 customers at the time.</p>
<p>I was pleased with my work. I was just months into my role at TinEye and I already had a material impact. Reflecting on this time almost two decades later, I realized that doing a good job at work was not what created the real impact. There are many people out there who are smarter and work harder who move the needle further at their jobs than I ever did.</p>
<p>The real impact of my work was realized when I asked my boss and co-founder of TinEye, Paul Bloore, if I could open source urllib3 under my own name with a permissive MIT license, and Paul said yes. I did not realize at the time how generous and rare this was, but I learned later after having worked with many companies who fought tooth and nail to retain and control every morsel of intellectual property they could get their hands on.</p>
<p>It’s one thing to write high impact code that helps ourselves or our employer, but it’s another thing to unlock it so that it can help millions of other people and organizations too.</p>
<p>Choosing a permissive open source license like MIT made Paul’s decision easy: There was no liability or threat to the company. TinEye had all the same rights to the code as I did or any other contributor did. In fact, Paul allowed me to continue improving it while I worked there because it benefited TinEye as much as it benefited everyone else.</p>
<p>Releasing urllib3 under my own name allowed me to continue maintaining and improving the project even after leaving, because it was not locked under my employer’s namespace and I felt more ownership over the project.</p>
<p>Hundreds of contributors started streaming in, too. Nobody loves maintaining a fork if they don’t have to, so it’s rational to report bugs upstream and supply improvements if we have them.</p>
<p>The growth of urllib3 since the first release in 2008 has been a complicated journey. Today, my role is more of a meta-maintainer where I support our active maintainers (thank you Seth M. Larson, Quentin Pradet, Illia Volochii!) while allowing people to transition into alumni maintainers over time as life circumstances change. It’s important to remember that while funding open source is very important and impactful (<a href="https://urllib3.readthedocs.io/en/latest/sponsors.html">please consider supporting urllib3</a>), it’s not always about money. People don’t want to work on one thing their whole life, so we have to allow for transition and succession.</p>
<p>I learned many lessons from my first big open source project, and I continue to apply them to all of my projects since then with great success. I hope you’ll join along!</p>

      ]]></content:encoded></item><item><title>Hanlon-Clarke Corollary</title><link>https://shazow.net/posts/hanlon-clarke-corollary/</link><pubDate>Wed, 30 Apr 2025 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/hanlon-clarke-corollary/</guid><description>&lt;p>I went into a deep research hole trying to discover the origin of the quote:&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;Any sufficiently advanced incompetence is indistinguishable from malice.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>tl;dr: Earliest online record of this quote is by Vernon Schryver on May 1, 2002.&lt;/p>
&lt;h2 id="greys-law-1996">Grey&amp;rsquo;s Law, 1996?&lt;/h2>
&lt;p>Wikipedia, LLMs, and various random blog posts cite it as &amp;ldquo;Grey&amp;rsquo;s Law&amp;rdquo; which I haven&amp;rsquo;t been able to corroborate.&lt;/p>
&lt;p>Wikipedia &lt;a href="https://en.wikipedia.org/wiki/Clarke%27s_three_laws#cite_note-Gooden2015-7">references a book from 2015&lt;/a> which refers to the quote as &amp;ldquo;Grey&amp;rsquo;s Law&amp;rdquo; but attributes it to &amp;ldquo;unknown author&amp;rdquo;. I couldn&amp;rsquo;t find an actual source for someone named Grey, and closest &amp;ldquo;Grey&amp;rdquo; reference I&amp;rsquo;ve found mentioned is a Youtuber named &lt;a href="https://en.wikipedia.org/wiki/CGP_Grey">CGP Grey from 2010+&lt;/a>.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>I went into a deep research hole trying to discover the origin of the quote:</p>
<blockquote>
<p>&ldquo;Any sufficiently advanced incompetence is indistinguishable from malice.&rdquo;</p>
</blockquote>
<p>tl;dr: Earliest online record of this quote is by Vernon Schryver on May 1, 2002.</p>
<h2 id="greys-law-1996">Grey&rsquo;s Law, 1996?</h2>
<p>Wikipedia, LLMs, and various random blog posts cite it as &ldquo;Grey&rsquo;s Law&rdquo; which I haven&rsquo;t been able to corroborate.</p>
<p>Wikipedia <a href="https://en.wikipedia.org/wiki/Clarke%27s_three_laws#cite_note-Gooden2015-7">references a book from 2015</a> which refers to the quote as &ldquo;Grey&rsquo;s Law&rdquo; but attributes it to &ldquo;unknown author&rdquo;. I couldn&rsquo;t find an actual source for someone named Grey, and closest &ldquo;Grey&rdquo; reference I&rsquo;ve found mentioned is a Youtuber named <a href="https://en.wikipedia.org/wiki/CGP_Grey">CGP Grey from 2010+</a>.</p>
<p>Meanwhile, Rationalwiki&rsquo;s Talk section on Grey&rsquo;s Law has <a href="https://rationalwiki.org/wiki/Talk:Grey's_Law">an anecdotal story</a> from someone who claims to have made that quote in the 90&rsquo;s under a handle related to Grey:</p>
<blockquote>
<p>[&hellip;] I have a distinct memory that some point in the very late 90&rsquo;s or early 00&rsquo;s was the first time (for me) that I posited the maxim &ldquo;any sufficiently advanced incompetence is indistinguishable from malice,&rdquo; with explicit crediting of Clarke and Hanlon. [&hellip;]</p>
</blockquote>
<p>Unfortunately, I haven&rsquo;t been able to find actual archives of posts with this quote from the 90s.</p>
<h2 id="vernon-schryver-2002">Vernon Schryver, 2002</h2>
<p>Earliest online record of this quote is by Vernon Schryver on May 1, 2002 on in the <code>news.admin.net-abuse.email usenet</code> newsgroup, which has sadly been purged by Google Groups but luckily archive.org still has a snapshot:
<a href="https://web.archive.org/web/20070221155843/http://groups.google.com/group/news.admin.net-abuse.email/msg/f9f67dca7591a860">https://web.archive.org/web/20070221155843/http://groups.google.com/group/news.admin.net-abuse.email/msg/f9f67dca7591a860</a></p>
<p><img src="05403fc7-2f9c-4b92-ba6f-ec6ed687c4c0" alt="Image"></p>
<h2 id="napoleon-clarke-law-2004">Napoleon-Clarke Law, 2004</h2>
<p>It was later dubbed the &ldquo;Napoleon-Clarke Law&rdquo; by Paul Ciszek on October 11, 2004 in the <code>rec.arts.sf.fandom</code> usenet group. Paul explains later in the same thread:</p>
<blockquote>
<p>Well, Napoleon said something about not attributing to malice that which is adequately explained by incomptence, and Clarke said that any sufficiently advanced technology is indistinguishable from magic. So far as I know I am the first one to put them together and call it the Napoleon-Clarke law.</p>
</blockquote>
<p>After finding some of the pieces pointing to Vernon Schryver and Paul Ciszek, I stumbled on this excellent overview thread by Kevin J. Maroney in Sept 25, 2005 on the <code>rec.arts.sf.fandom</code> newsgroup that is worth a read: <a href="https://groups.google.com/g/rec.arts.sf.fandom/c/1JaqxckrAOc/m/xO5jzyejK1UJ">https://groups.google.com/g/rec.arts.sf.fandom/c/1JaqxckrAOc/m/xO5jzyejK1UJ</a></p>
<h2 id="hanlon-clarke-corrolary">Hanlon-Clarke Corrolary</h2>
<p>We later reattributed Napoleon&rsquo;s quote to <a href="https://en.wikipedia.org/wiki/Hanlon%27s_razor">Hanlon&rsquo;s Razor</a>.</p>
<p>Coming back full circle with Grey&rsquo;s anecdote who correctly attributed the mashup to Hanlon and Clarke, I&rsquo;m going to take this opportunity to further rebrand Vernon&rsquo;s quote as the Hanlon-Clarke Corollary.</p>
<p>Until we can find better corroboration of Grey&rsquo;s anecdote (please reach out if you have a lead!), I say we attribute this quote mashup to Vernon Schryver.</p>
<blockquote>
<p>Hanlon-Clarke Corollary: &ldquo;Any sufficiently advanced incompetence is indistinguishable from malice.&rdquo;
~ Vernon Schryver, 2002</p>
</blockquote>

      ]]></content:encoded></item><item><title>How can open social protocols fail us in 2025</title><link>https://shazow.net/posts/open-social-2025/</link><pubDate>Mon, 31 Mar 2025 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/open-social-2025/</guid><description>&lt;p>Let&amp;rsquo;s compare the possible failure modes of various open social protocols:&lt;/p>
&lt;ul>
&lt;li>ActivityPub/Mastodon&lt;/li>
&lt;li>ATProto/Bluesky&lt;/li>
&lt;li>Farcaster/Warpcast&lt;/li>
&lt;/ul>
&lt;p>Some scenarios I&amp;rsquo;d like to consider:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Can my identity be taken away?&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Can my audience be taken away?&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Can my ability to extend it and build on the protocol be taken away?&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>Some specific failure modes I&amp;rsquo;m concerned about:&lt;/p>
&lt;ol start="4">
&lt;li>&lt;strong>What happens when the most popular app or instance becomes malicious?&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Is there something the maintainer can do that would prompt me to abandon the service/protocol altogether?&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>Let&amp;rsquo;s begin:&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>Let&rsquo;s compare the possible failure modes of various open social protocols:</p>
<ul>
<li>ActivityPub/Mastodon</li>
<li>ATProto/Bluesky</li>
<li>Farcaster/Warpcast</li>
</ul>
<p>Some scenarios I&rsquo;d like to consider:</p>
<ol>
<li><strong>Can my identity be taken away?</strong></li>
<li><strong>Can my audience be taken away?</strong></li>
<li><strong>Can my ability to extend it and build on the protocol be taken away?</strong></li>
</ol>
<p>Some specific failure modes I&rsquo;m concerned about:</p>
<ol start="4">
<li><strong>What happens when the most popular app or instance becomes malicious?</strong></li>
<li><strong>Is there something the maintainer can do that would prompt me to abandon the service/protocol altogether?</strong></li>
</ol>
<p>Let&rsquo;s begin:</p>
<h2 id="activitypubmastodon">ActivityPub/Mastodon</h2>
<p>There are two scenarios to consider here: One is for self-hosting and second is accounts that are hosted on a shared instance.</p>
<p>While ActivityPub has many uses outside of social networks and the social network aspects like Mastodon are <em>capable</em> of self-hosting, they are practically designed for shared hosting<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>: It is practically impossible for everyone to self-host an instance today. Even if I had a magic wand and made every Mastodon account self-hosted, the service as designed today would be unusable.</p>
<p>With that in mind, I will primarily focus on the shared-hosted Mastodon-compatible social network instance case specifically:</p>
<ol>
<li>
<p><strong>Can my identity be taken away?</strong></p>
<p>💀 Yes.</p>
<p>The owner of the instance has full control over instance accounts and can do with them what they please.</p>
</li>
<li>
<p><strong>Can my audience be taken away?</strong></p>
<p>💀 Yes.</p>
<p>In fact, this happened to me personally. I was on an instance with a few hundred followers, then the admin burned out and abruptly shut down the server and now both the identity and audience are forever gone, impossible for me to recover.</p>
</li>
<li>
<p><strong>Can my ability to extend it and build on the protocol be taken away?</strong></p>
<p>💚 No.</p>
<p>ActivityPub is an open and generously permissive protocol built around implicit trust, almost to a fault with some security implications here such as private accounts&rsquo; posts getting leaked across federated boundaries<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
</li>
<li>
<p><strong>What happens when the most popular app or instance becomes malicious?</strong></p>
<p>🚨 Worst case scenario is if the largest instance disappears (or gets purchased by a malicious billionaire) then all of those identities are gone, all that audience is gone, the content is gone (some may be recoverable through caches on federated instances, but not robustly attributable). <em>But</em> other instances survive and are largely unaffected except for the reduction in audience, and this is an improvement over something like Twitter.</p>
</li>
<li>
<p><strong>Is there something the maintainer can do that would prompt me to abandon the service/protocol altogether?</strong>
💚 I don&rsquo;t think so.</p>
<p>There is a huge ecosystem with diverse implementations by varieties of people. There are already apps built on ActivityPub/Mastodon that span every conceivable ideology, including TruthSocial that is owned by the president of the United States and Threads owned by Meta. I don&rsquo;t think any of them can say or do much which would severely change my perspective on ActivityPub or Mastodon as a technology.</p>
<p>I found at least 80 independent Mastodon instances with over 10,000 users and 10 with over 100,000 users. Protocol challenges aside, this level of instance diversity is very impressive.</p>
</li>
</ol>
<h2 id="atprotobluesky">ATProto/Bluesky</h2>
<p><a href="https://bsky.app/">Bluesky</a> (and the ATProto protocol it&rsquo;s built on) has many optional provisions for improving the sovereignty of users, such as managing signing keys with the <em>Public Ledger of Credentials</em> (<a href="https://web.plc.directory/">PLC</a>)<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> and running my own <em>Personal Data Server</em> (<a href="https://docs.bsky.app/docs/advanced-guides/federation-architecture">PDS</a>)<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> that can serve as a source of truth for my data. While this is not the default, and it&rsquo;s unrealistic for everyone to self-host a PDS, the protocol <em>is</em> designed for a world where everyone has ownership over their signing key and it does support alternate identity schemes in the future (potentially even fully sovereign and programmable onchain signatures).</p>
<ol>
<li>
<p><strong>Can my identity be taken away?</strong></p>
<p>💚 No-ish.</p>
<p>The default onboarding with a <code>*.bsky.social</code> identity is controlled by Bluesky and can be taken away, but the protocol facilitates using my own domain for my identity, so I can be <code>shazow.net</code> instead of <code>shazow.bsky.social</code> and that can&rsquo;t be taken away by Bluesky <strong>BUT</strong> it can be taken away by ICANN and my registrar.</p>
<p>Note that this namespace is mostly cosmetic, the actual protocol interactions are mapped to a long-lived public key (a <a href="https://docs.bsky.app/docs/advanced-guides/resolving-identities">DID, managed by the PLC</a>) and the &ldquo;human readable&rdquo; identity is simply an alias to it which can be changed without losing the interaction integrity. This mapping is currently stored in a verifiable but centralized database (<a href="https://plc.directory/">PLC Directory</a>) which can misbehave by preventing updates, limiting access to the ledger logs, and <a href="https://github.com/did-method-plc/did-method-plc/issues/86">removing/reordering the history of operations</a>.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
<p>Overall, if my handle is taken away, then someone else will show up as me on Bluesky and I will need to register another handle, but I will not lose any of my social graph. If the PLC Directory disappears, I could lose my ability to write to my account.</p>
</li>
<li>
<p><strong>Can my audience be taken away?</strong></p>
<p>🟠 Sort of.</p>
<p>ATProto messages are designed to be robustly replicated with cryptographic signatures, so there can be arbitrary paths to getting messages to my audience. Unlike ActivityPub, if one replica loses all of my posts then another replica can robustly re-introduce them to the network without risk of forgery.</p>
<p>On the other hand, a core part of Bluesky is their approach to <a href="https://bsky.social/about/blog/4-13-2023-moderation">composable and comprehensive moderation tooling</a> that is available at many layers (the client via labels, the AppView, the relay, etc). On multiple occasions, Bluesky chose to deplatform users who effectively lost all access to their audience.</p>
<p>It would help if Bluesky leaned more heavily on their excellent community moderation tools and eventually remove global moderation altogether. Additional popular clients and AppViews with their own policies would help, too.</p>
</li>
<li>
<p><strong>Can my ability to extend it and build on the protocol be taken away?</strong></p>
<p>💚 No.</p>
<p>I can extend ATProto with my own &ldquo;lexicons&rdquo; and create whole parallel functionality within the same infrastructure.</p>
<p>In fact, there are already many interesting use cases being used, like: <a href="https://whtwnd.com/">WhiteWind</a> for blogging, <a href="https://smokesignal.events/">Smoke Signal</a> for coordinating events, <a href="https://bsky.app/profile/flashes.blue">Flashes App</a> for photo sharing, and <a href="https://github.com/fishttp/awesome-bluesky">many other experiments</a>. That said, I suspect some of this will change in the future as more griefing and other attacks on the protocol are explored.</p>
</li>
<li>
<p><strong>What happens when the most popular app or instance becomes malicious?</strong></p>
<p>🚨 What happens if Bluesky Social, PBC (the company behind Bluesky) disappears tomorrow? Let&rsquo;s say bsky.app and associated infrastructure they run is gone (bsky.social PDS, AppView, relays, etc), now what?</p>
<p>The good: The <a href="https://github.com/bluesky-social">Bluesky App and infrastructure tools are all permissively-licensed open source</a>, and someone else can fork them and release alternate versions. There are some minor players already trying this. The historic state can be replayed from any archives people have (there are some services who maintain snapshots).</p>
<p>The bad: It&rsquo;s extremely expensive to operate for the current userbase (33M users today), it was <a href="https://lobste.rs/s/gsj2e2/how_self_host_all_bluesky_except_appview#c_9cavzb">estimated to cost over $500,000/mo in hardware costs</a> when Bluesky was half this size. Assuming double that after bandwidth, human, and other costs, it&rsquo;s prohibitively expensive for a random individual to take on that cost and it&rsquo;s too much to quickly coordinate a grassroots cooperative effort. Frankly, both the protocol (relays feeding an AppView monolith) <em>and</em> culture (&ldquo;signups should be free&rdquo;) are not designed for a collectively owned/operated ecosystem at scale.</p>
</li>
<li>
<p><strong>Is there something the maintainer can do that would prompt me to abandon the service/protocol altogether?</strong></p>
<p>🚨 This is a tricky one.</p>
<p>If the Bluesky team said tomorrow &ldquo;you know what? we&rsquo;re tired, this isn&rsquo;t working out, we&rsquo;re going to stop working on it&rdquo; then I&rsquo;m not confident that someone else would pick up the baton. That could very well spell the end of the social network as it is today. It&rsquo;s not like there&rsquo;s a bunch of companies making good money from it that they&rsquo;d be compelled to take over to retain their business. If there were more independent and institutionally motivated companies hosting their own instances of the whole stack so that users can trivially switch endpoints on the app, I&rsquo;d feel more confident.</p>
</li>
</ol>
<h2 id="farcasterwarpcast">Farcaster/Warpcast</h2>
<p><a href="https://www.farcaster.xyz/">Warpcast (and the Farcaster protocol)</a>  takes a similar portable cryptographically-signed approach as ATProto, except with several important differences: Full identity sovereignty, native payments, and a culture around building an active self-reinforcing economy within the protocol ecosystem. Both third-party services and unique features like mini-apps that take advantage of payments being a native mechanism on Farcaster. Keep this in mind for the final point.</p>
<ol>
<li>
<p><strong>Can my identity be taken away?</strong></p>
<p>💚 No.</p>
<p>While the default onboarding flow uses a custodied identity similar to Bluesky (Farcaster ID), anyone can set <a href="https://unchainedcrypto.com/ethereum-name-service-ens/">an ENS identity</a> which is fully self-custodied and can&rsquo;t be taken away even, not even by ICANN. So instead of <code>shazow.farcaster</code> I am <code>shazow.eth</code>. This also allows for arbitrary programmable collectively owned namespaces, since the ENS can be owned and managed by any smart contract.</p>
<p>Additionally, Farcaster&rsquo;s key management is onchain in a smart contract <em>by default</em>, so every user has full control over their permissions in a censorship-resistant way<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>. There are several supported signing formats, including passkeys. There is also optional social recovery available, by default it allows Warpcast to help recover my account if I lose access to all my signers (passkey, mobile app, etc) but I can change that.</p>
</li>
<li>
<p><strong>Can my audience be taken away?</strong></p>
<p>💚 Not entirely, but there is still only one main app (Warpcast) that is used by the vast majority of people.</p>
<p>The dominant app can effectively moderate people out of the timeline, indistinguishable from being categorized as spam. This will change very soon as large companies like Coinbase are integrating Farcaster into their apps which would compete in scale with the active audience on the network today. A few more large participants with a variety of timeline/moderation approaches will make audience ownership relatively safe.</p>
</li>
<li>
<p><strong>Can my ability to extend it and build on the protocol be taken away?</strong></p>
<p>💚 No.</p>
<p>While the apps do a great job hiding the implementation details, the key management is non-custodial in a smart contract, so I can fully control who gets write access to my namespace and that can&rsquo;t be taken away from me. The Farcaster Hub protocol is similarly cryptographically signed so my messages can be routed in arbitrary ways even with one node censoring.</p>
<p>While lot of development today is happening around <a href="https://miniapps.farcaster.xyz/">Mini-Apps (formerly known as Frames)</a> which run within the timeline with native access to payment and social functions, there is also a <a href="https://github.com/a16z/awesome-farcaster">healthy community building on the Farcaster protocol itself</a>.</p>
</li>
<li>
<p><strong>What happens when the most popular app or instance becomes malicious?</strong></p>
<p>🚨 What happens if Merkle Manufactory (company behind Farcaster) disappears tomorrow, along with the app and all the infrastructure they run?</p>
<p>While <a href="https://github.com/farcasterxyz">the protocol and hubs to run it are permissively licensed open source</a>, the main app is closed source and much of the auxiliary infrastructure required to operate the app is also closed source (algorithmic timeline service, spam moderation, etc). There are some alternate implementations, but it&rsquo;s still early.</p>
<p>On the other hand: It&rsquo;s not wildly expensive to operate the necessary infrastructure ($X00/mo operate a hub), largely in part because <a href="https://dune.com/pixelhack/farcaster">the Farcaster network is still fairly small</a> (3% of Bluesky right now, at 870K users and 45K daily actives), so it&rsquo;s quite plausible that individuals, small groups, or another company could take on this burden. Plus there is a thriving culture of economic activity on Farcaster. Coinbase is integrating a client, Neynar provides paid API services for developers, a bunch of people are almost making a living through various mini-app activities.</p>
<p>This scenario would be painful, but the proportion of Farcaster&rsquo;s size to the economic activity on the protocol is still promising. This may change as Farcaster grows.</p>
</li>
<li>
<p><strong>Is there something the maintainer can do that would prompt me to abandon the service/protocol altogether?</strong></p>
<p>🚨 This one is also tricky. I suspect there is not yet enough momentum if the founders decided to wind down. I ask myself if I would care if a megacorp acquired the team along with the most popular app but committed to maintaining the same design properties&hellip; I&rsquo;m not sure! More independent/open source, and economically motivated apps platforming large audiences would make me feel more confident that moderation won&rsquo;t get abused.</p>
</li>
</ol>
<h2 id="takeaway">Takeaway</h2>
<h3 id="economic-balance-matters">Economic balance matters</h3>
<p>If it&rsquo;s far too costly to bootstrap infrastructure in a black swan event (censorship, evil billionaire, physical infrastructure failure, whatever) then the efficacy of a robust protocol is lost.</p>
<p>For a given player, the balance is between the cost of correcting a failure and value that is gained from recovering. If a network has a thriving economy and it is comparatively inexpensive to operate it, then we can be more assured that it will persevere.</p>
<h3 id="scale-matters">Scale matters</h3>
<p>Some protocols have desirable properties when the network is small, but lose them at scale:</p>
<p>A few blogs federating ActivityPub remain perfectly robust from capture, but a million user Mastodon instance is a juicy point of failure. Even smaller instances are brittle and risky: perhaps the sysadmin gets burned out and wants to do something else or a security update isn&rsquo;t installed quickly enough.</p>
<p>Similarly, Farcaster seems almost sustainable at today&rsquo;s scale, but will it be off-balance like Bluesky if it grows to tens of millions of users?</p>
<h3 id="interdependence-matters">Interdependence matters</h3>
<p>We can&rsquo;t expect a single dominant player to resist exercising powers like threats of censorship, even if the underlying protocol is resistant. If one player is platforming 99% of the network, there is nothing stopping them from abandoning the underlying protocol altogether and just replacing it with a private database instead. We must have a multi-polar plurality of interdependent players to protect us from the effects of capture.</p>
<h2 id="whats-next">What&rsquo;s next?</h2>
<p>I&rsquo;d like to see each protocol describe what their ideal evolution and adoption looks like a year from today. If everything about the current roadmap goes right and all the relevant partners enthusiastically join forces, what does the world look like for this protocol in 2026?</p>
<hr>
<p><em>Thanks to <a href="https://bmann.ca/">Boris</a>, <a href="https://boscolo.co/">Boscolo</a>, and <a href="https://warpcast.com/leewardbound">Leeward</a> for reviewing early drafts of this post. Also big thanks to everyone who asked great questions after this was published.</em></p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://mastodon-analytics.com/">The Mastodon fediverse has around 9.5M registered and almost 1M monthly active users across 8700 instances</a>. The number of single-user instances out there must be a rounding error over the total number of users across Mastodon. I was only able to find a few dozen instances with 3 or fewer users.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>There is a fundamental assumption in ActivityPub based social networks that federated instances are trustworthy sources and custodians of data. If another instance misbehaves, <a href="https://fokus.cool/2025/03/25/pixelfed-vulnerability.html">such as accidentally leaking private account posts</a>, then the expected recourse is that instances will notice this and defederate from them. Unfortunately, by that point, the damage has already been done. It&rsquo;s a system that relies on constant vigilance and good behaviour by administrators of instances.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Public Ledger of Credentials, the directory for mapping these credentials, is currently a service operated by the Bluesky team. For people familiar with rollups, it&rsquo;s like having a centralized data availability and centralized sequencer, but with (self-certifying) verifiable state transitions. If the Bluesky team becomes malicious, another team can fork the PLC state at an earlier checkpoint and manage it differently, but the AppView would still need to decide which PLC to obey. I hope this design is improved in the future.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>One of my favourite parts about Bluesky&rsquo;s architecture design is that it&rsquo;s very layered with conceptually optional optimizations. At the very bottom sits the Personal Data Server which is itself enough to bootstrap a basic social network from other such people. Above it are the relays, which aggregate many PDS into a composite firehose. Above the relays is the AppView, which creates the timelines that are served to the app&rsquo;s end users. Right now the PDS implementation is coupled with the signing key, but conceptually they could be separate.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>If the PLC Directory disappears today, this would be quite catastrophic if a replica of the ledger&rsquo;s logs is not available. This is the authoritative source for who is allowed to update what, and without it the cryptographic permission system is broken. Another great usecase for an onchain smart contract, which would also help with update censorship and limiting access to the latest verifiable state.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Censorship-resistance here means that I can&rsquo;t be stopped from submitting an update to my settings, and no one can be prevented from accessing the latest verified state of my settings.&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

      ]]></content:encoded></item><item><title>Post-Growth</title><link>https://shazow.net/posts/post-growth/</link><pubDate>Tue, 21 Jan 2025 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/post-growth/</guid><description>&lt;p>Imagine thousands of years in the future, when humanity is dispersed across the stars. We are fully actualized and have reached the final leafs of the universe&amp;rsquo;s tech tree. We are happy and in control of our destiny.&lt;/p>
&lt;p>Are the trillions of us spanning many solar systems?&lt;/p>
&lt;p>What if there are only millions, or even thousands of us?&lt;/p>
&lt;p>It&amp;rsquo;s tempting to take our history of swarming through continents on Earth, consuming all available resources to scale and grow, and extrapolate it into space. Many planets across many solar systems, each with many billions of people. A lot of science fiction paints this version of the future, but what would it take for us to imagine a different future?&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>Imagine thousands of years in the future, when humanity is dispersed across the stars. We are fully actualized and have reached the final leafs of the universe&rsquo;s tech tree. We are happy and in control of our destiny.</p>
<p>Are the trillions of us spanning many solar systems?</p>
<p>What if there are only millions, or even thousands of us?</p>
<p>It&rsquo;s tempting to take our history of swarming through continents on Earth, consuming all available resources to scale and grow, and extrapolate it into space. Many planets across many solar systems, each with many billions of people. A lot of science fiction paints this version of the future, but what would it take for us to imagine a different future?</p>
<p>What if we defeated death and could choose to live forever? What if we become cybernetic, melding with the machines we build? What if we tame our minds and upload replicas into digital storage, onto digital simulations, and back into artificial physical sleeves?</p>
<p>What if we transcend life, creating artificial beings beyond imagination? What if we finally achieve alchemy: transmute matter into energy and back again, transporting at the speed of light and reconstructing into whatever physical bodies we want whenever we want?</p>
<p>Imagine a distant species, once human but now anything, which can exist and scale to any shape or quantity on a whim. A species that is confident of its mastery over reality, unneeding of redundancy and excess.</p>
<p>Would there be thousands of us one day, travelling in groups as we wish? Materializing peers and family on another day into billions, only to pack up later into few again.</p>
<p>Would some choose to join their minds into a collective consciousnesses, blurring the line of enumeration?</p>
<p>Will there be one?</p>
<hr>
<p>For a long time, we have been in an <em>Age of Growth</em> as a society. Growth has been our main metric for success that we optimized obsessively, sometimes to toxic extremes.</p>
<p>Can we imagine what it would take for us to transcend into a post-growth society?</p>
<hr>
<p>Thank you to John for sending this related audio short story after reading this: <a href="https://www.youtube.com/watch?v=5UxUS6bPiT8">Outlasting the Universe</a></p>

      ]]></content:encoded></item><item><title>Conditioning</title><link>https://shazow.net/posts/conditioning/</link><pubDate>Mon, 20 Jan 2025 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/conditioning/</guid><description>&lt;p>I think of conditioning as a long-term conversation between decentralized components.&lt;/p>
&lt;p>After breaking my foot, I&amp;rsquo;ve been working on conditioning all of the tiny ligaments to properly support movements and impacts I haven&amp;rsquo;t practiced in months. I can&amp;rsquo;t just say &amp;ldquo;hey foot, you&amp;rsquo;re not broken anymore, go back to normal.&amp;rdquo; I have to slowly and incrementally reinforce each change.&lt;/p>
&lt;p>Similar to learning a musical instrument by training our fingers to move a specific way, over and over &amp;ndash; we&amp;rsquo;re telling all of the related &amp;ldquo;muscle memory&amp;rdquo; that this is an important motion we need to be good at. Like lifting weights, we&amp;rsquo;re telling the muscle &amp;ldquo;you&amp;rsquo;re going to continually exceed your capacity, so get bigger and stronger.&amp;rdquo;&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>I think of conditioning as a long-term conversation between decentralized components.</p>
<p>After breaking my foot, I&rsquo;ve been working on conditioning all of the tiny ligaments to properly support movements and impacts I haven&rsquo;t practiced in months. I can&rsquo;t just say &ldquo;hey foot, you&rsquo;re not broken anymore, go back to normal.&rdquo; I have to slowly and incrementally reinforce each change.</p>
<p>Similar to learning a musical instrument by training our fingers to move a specific way, over and over &ndash; we&rsquo;re telling all of the related &ldquo;muscle memory&rdquo; that this is an important motion we need to be good at. Like lifting weights, we&rsquo;re telling the muscle &ldquo;you&rsquo;re going to continually exceed your capacity, so get bigger and stronger.&rdquo;</p>
<p>It&rsquo;s not just a one-and-done: Conditioning takes time and repetition, because the channel is low-bandwidth and lossy, and the micro-changes required are many and sequential.</p>
<p>When we train our dog to not be reactive about visitors (or to salivate when the bell rings), we can use positive reinforcement to rewire the brain with the help of oxytocin signals. Treats, &ldquo;good dog&rdquo;, or even hugs can all be forms of communicating that the precursor to some stressor is actually okay. Just like humans working with a therapist, we try to condition the sources of our anxieties to take on different frames (or acclimate to our fears).</p>
<p>Similar to positive reinforcement with oxytocin, there is cortisol and dopamine and other hormones/neurotransmitters which communicate desirable/undesirable conditioning between different aspects of our minds and bodies.</p>
<p>I really like the model that we&rsquo;re not monoliths, but rather a bunch of disparate components who are stuck together. Our brain can&rsquo;t simply tell our body to shed fat and build muscle, our tic disorder to chill out, our ligaments to support wider range of motion&hellip; but our brain can slowly and methodically facilitate these changes by communicating them through conditioning!</p>
<p>What does this mean for society?</p>
<p>If we break out of the monolith mindset of our bodies, we can also think of communities, corporations, nations, species as &ldquo;mega organisms&rdquo; who suffer from inefficient/indirect communication.</p>
<p>We may condition our societies towards democratic practices or into accepting authoritarian rule. To justice or inequity. It can happen slowly through reinforcement, or it can snap like a bone breaking from ever-increasing pressure. Those suffering may ache like a festering infection, but we can acclimate (or amputate).</p>
<p>What&rsquo;s the difference between Jeff Bezos and a rando who woke up with a hundred billion dollars?</p>
<p>Bezos has preconditioned infrastructure to achieve his endeavours. He can take on logistics, media, space exploration, investment, and who knows what else. What can rando do without spending months/years &ldquo;conditioning&rdquo; their assets to support their endeavours? Just think of how long it would take to even hire a sizeable team of good people! This is conditioning.</p>
<p>In many ways, the peripersonal space of powerful individuals expands far beyond their wealth. It extends to that which they have conditioned to advance their pursuits.</p>
<p>I want us to condition ourselves out of thinking like monoliths. How do we do that?</p>
<p>We should condition ourselves to think in terms of conditioning, rather than quick drastic swings that bring injury after injury. (&ldquo;We need to be good at lifting heavy weights, so we should lift the heavy weights immediately.&rdquo;)</p>
<p>We can set ambitious goals that are beyond the ability of a single component, or even a single person or nation, and we need to figure out how to communicate them properly to all participants&ndash;repeatedly, consistently! All participants need to practice them, repeatedly and consistently!</p>
<p>Let&rsquo;s not ignore our pains, but take time to listen and understand where the pain is really coming from and why, and how to address it.</p>
<p>But most importantly: Recognize when we&rsquo;re being conditioned into something we don&rsquo;t want to be.</p>

      ]]></content:encoded></item><item><title>1231</title><link>https://shazow.net/posts/1231/</link><pubDate>Fri, 08 Nov 2024 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/1231/</guid><description>&lt;p>I wrote down the names of 5 friends, I&amp;rsquo;m going to send each of them a gratitude email by the end of the year. Want to join me?&lt;/p>
&lt;p>It can be just a couple of sentences of how they positively impacted your life, or a brief story of a positive shared experience, or what you admire about them.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>I wrote down the names of 5 friends, I&rsquo;m going to send each of them a gratitude email by the end of the year. Want to join me?</p>
<p>It can be just a couple of sentences of how they positively impacted your life, or a brief story of a positive shared experience, or what you admire about them.</p>

      ]]></content:encoded></item><item><title>Why Blockchain?</title><link>https://shazow.net/posts/why-blockchain/</link><pubDate>Fri, 24 May 2024 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/why-blockchain/</guid><description>&lt;p>A blockchain like Ethereum&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup> provides a collectively owned programmable substrate, allowing us to build more decentralized &amp;ldquo;onchain&amp;rdquo; applications where the consumers have more agency over how things behave.&lt;/p>
&lt;p>Let&amp;rsquo;s break down &amp;ldquo;collectively owned programmable substrate&amp;rdquo;:&lt;/p>
&lt;h2 id="collectively-owned">Collectively Owned&lt;/h2>
&lt;p>The Ethereum consensus is a &lt;strong>communal mechanism&lt;/strong> which enforces certain rights for anyone who chooses to use it.&lt;/p>
&lt;p>Primarily, it is designed to enforce the right to deploy code that is guaranteed to be executed correctly without relying on an empowered intermediary (someone who can choose to break the rules we&amp;rsquo;ve established). These rights are extended to everyone equally: regardless of which country or jurisdiction you live in, and whether you&amp;rsquo;re a person or corporation or even a program.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>A blockchain like Ethereum<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> provides a collectively owned programmable substrate, allowing us to build more decentralized &ldquo;onchain&rdquo; applications where the consumers have more agency over how things behave.</p>
<p>Let&rsquo;s break down &ldquo;collectively owned programmable substrate&rdquo;:</p>
<h2 id="collectively-owned">Collectively Owned</h2>
<p>The Ethereum consensus is a <strong>communal mechanism</strong> which enforces certain rights for anyone who chooses to use it.</p>
<p>Primarily, it is designed to enforce the right to deploy code that is guaranteed to be executed correctly without relying on an empowered intermediary (someone who can choose to break the rules we&rsquo;ve established). These rights are extended to everyone equally: regardless of which country or jurisdiction you live in, and whether you&rsquo;re a person or corporation or even a program.</p>
<p>Anyone can read the state of the Ethereum blockchain, but writing to the blockchain requires paying for the cost of the execution<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<h2 id="programmable-substrate">Programmable Substrate</h2>
<p>Building <em>on top of</em> a programmable substrate inverts many of our traditional assumptions:</p>
<ul>
<li>We can create programs that are immutable (permanent and unchangeable) yet flexible.</li>
<li>We can create programs that give up all owner control, giving all of the power to the consumers.</li>
<li>We can build composeable components that can be reused safely (because they&rsquo;re immutable).</li>
</ul>
<h3 id="programmable-security">Programmable Security</h3>
<p>Traditionally, services we use have concrete security flows: Choose an email and password; Add a phone number for a second factor; Upload a photo of our driver&rsquo;s license to verify it&rsquo;s us. If our bank doesn&rsquo;t support Authy for a second factor, there&rsquo;s nothing we can do but complain. If our email provider doesn&rsquo;t support team logins, there&rsquo;s nothing we can do but share the password with the team.</p>
<p>The blockchain ecosystem flips this dynamic: The application does not need to care how the consumer secures their accounts. Every account can provide a cryptographic signature, and that&rsquo;s enough.</p>
<p>With smart contract wallets like <a href="https://safe.global/">Safe{Wallet}</a>, we can customize every aspect of our own security. We can add multiple devices, we can add spending limits, we can add various recovery schemes like <a href="https://safe.mirror.xyz/WxKSxD9J1bRI-SDOuDvAAIezwVrvWWkpuwuzcLDPSmk">Social Recovery</a>. This allows us to dramatically innovate on our security practices as an independent industry.</p>
<h3 id="programmable-identity">Programmable Identity</h3>
<p>Traditionally, we have identity cards issued by our local government, or domains managed by ICANN, or a profile managed by LinkedIn. These are all empowered intermediaries: They can choose to deny changes, or outright ban us from their systems.</p>
<p>Ethereum Name Service (<a href="https://ens.domains/">ENS</a>) is an example of a collectively owned identity resolving system that is also fully programmable. We can set it up a name like <code>foo.eth</code> that routes one way on weekdays, and other ways on weekends. There is no centrally managed &ldquo;API&rdquo; that can be sunset and prevent us from using it like before. By using programmable security, we can abstract the ownership of our ENS to be managed by an individual, or a team, or a democratic collective, or anything else that can be expressed as a program.</p>
<h3 id="programmable-banking">Programmable Banking</h3>
<p>Traditionally, banks require us to pay bills from a Checking Account, but encourage us to hold money in a Savings Account through modest interest rates. Autopay Bills can only be paid out of Checking Accounts. If the Checking Account drops below zero, we get charged an Overdraft Fee (even if money is available in the Savings Account), and we&rsquo;ll also get hit by a Late Fee from the bill provider. Not to mention many things being limited to business hours, out of network ATM fees, excessive foreign exchange fees hidden in the price spread, over a billion people blocked out of the banking system at large. Why do we tolerate these anti-consumer constraints?</p>
<p>Banking in a collectively owned programmable substrate is quite different: There are no limits to how accounts must behave, we can automatically split incoming payments among our collaborators (e.g. <a href="https://splits.org/">splits.org</a>); we can setup a cascading system that pays for our services however we want&ndash;even complex vesting or payroll payments streamed down to the second (e.g. <a href="https://www.superfluid.finance/">superfluid.finance</a>, <a href="https://sablier.com/">sablier</a>); we can completely abstract multitudes of currencies and assets (e.g. <a href="https://swap.cow.fi/">cowswap</a>), allowing us to pay with whatever we prefer while being confident that we&rsquo;re receiving the most competitive rate that anyone else would too.</p>
<h3 id="programmable-everything">Programmable Everything?</h3>
<p>If you&rsquo;re a web developer, how many times have you built the same signup/login flow?</p>
<p>What would we build if we didn&rsquo;t have to re-implement standardized &ldquo;smart&rdquo; components that are collectively owned? What if our consumers could bring their own&hellip;</p>
<ul>
<li>Login security and recovery</li>
<li>Identity details</li>
<li>Payment processing</li>
<li>Social network audiences</li>
<li>Cosmetic assets (picture profiles, avatars, item skins, colour schemes and themes)</li>
<li>Privacy-preserving attested facts (whether they&rsquo;re over some age, or have some amount of income, or a citizen of some jurisdiction, or a unique human)</li>
<li>Governance processes for how communities make decisions</li>
<li>&hellip; What else?</li>
</ul>
<p>What if all of those components kept getting better and better, while our application could stay the same?</p>
<p>The question is not &ldquo;why can&rsquo;t we build this without a blockchain?&rdquo;</p>
<p>The question is: <strong>Why would we want to build things without a collectively owned programmable substrate?</strong></p>
<hr>
<div class="notice note">
    <strong>Disclosure</strong>
    <p>I work on projects in the Ethereum ecosystem and hold ETH.</p>
</div>

<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>There are many blockchains today with varying credibility and decentralization properties. Very few of them prioritize decentralization in the same way that Ethereum does, so that is my primary example for talking about what&rsquo;s possible.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>The fee for executing a transaction on Ethereum is referred to as the &ldquo;gas fee&rdquo;, and it is denominated in Ether. It is designed to closely proxy the real cost of the transaction to the Ethereum consensus and network, in order to prevent denial of service attacks. Any time something costs more on one side than another side, this can be an attack vulnerability or a potential externality which can be profitable to extract. Spam is a great example of this: To be effective, spam must cost far less to produce it than the profit that can be extracted from the externality it creates.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>

      ]]></content:encoded></item><item><title>Trustless</title><link>https://shazow.net/posts/trustless/</link><pubDate>Tue, 07 Nov 2023 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/trustless/</guid><description>&lt;blockquote>
&lt;p>&amp;ldquo;There are no such thing as zero trust assumptions.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>Trustless&lt;/em> is a confusing word: We don&amp;rsquo;t use it in common speech; it means &amp;ldquo;not trust worthy&amp;rdquo; in the dictionary; decentralized technologies sometimes use it as &amp;ldquo;not needing to trust&amp;rdquo;; and the uninitiated might interpret it to mean that it&amp;rsquo;s advocating for less trust?&lt;/p>
&lt;p>Despite the confusion, there is a very useful insight behind the concept of &amp;ldquo;trustless&amp;rdquo; (in the &lt;a href="https://shazow.net/posts/decentralization">decentralization&lt;/a> sense). To understand it, let&amp;rsquo;s suspend our immediate reaction and break down two interesting properties behind this concept: &lt;strong>Explicit trust and immutable trust.&lt;/strong>&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <blockquote>
<p>&ldquo;There are no such thing as zero trust assumptions.&rdquo;</p>
</blockquote>
<p><em>Trustless</em> is a confusing word: We don&rsquo;t use it in common speech; it means &ldquo;not trust worthy&rdquo; in the dictionary; decentralized technologies sometimes use it as &ldquo;not needing to trust&rdquo;; and the uninitiated might interpret it to mean that it&rsquo;s advocating for less trust?</p>
<p>Despite the confusion, there is a very useful insight behind the concept of &ldquo;trustless&rdquo; (in the <a href="https://shazow.net/posts/decentralization">decentralization</a> sense). To understand it, let&rsquo;s suspend our immediate reaction and break down two interesting properties behind this concept: <strong>Explicit trust and immutable trust.</strong></p>
<p>Our goal is not to reduce the amount of trust in the world, but rather to create <strong>more and better trust</strong> that we can use to create more powerful and interesting systems.</p>
<p>Consider a typical intimate relationship: We can create more and better trust through communication&ndash;setting expectations and being clear about our boundaries, allowing us to thrive even in more complicated and non-traditional arrangements.</p>
<p>Let&rsquo;s try to build a framework for doing this when designing other kinds of systems.</p>
<h2 id="immutable-and-mutable">Immutable and Mutable</h2>
<p>To trust something mutable is to understand that it can change. To trust something immutable is to understand that it cannot change. We&rsquo;ll explore how we can plan for and benefit from both scenarios.</p>
<p>When the CEO of a company makes a promise, we need to remember that it&rsquo;s not the individual making a lifelong commitment. The Chief Executive Officer is a <em>role</em> within a company. One day it can be Dick, next day it can be Parag, then Jack, then suddenly Elon.</p>
<p>A leadership role within a company is mutable, which means that the direction of a company can change. When we put our trust in a verbal commitment of a company, perhaps drafted by a PR firm, we must remember that the role which drives that promise can be changed&ndash;and inevitably will be, as we don&rsquo;t live forever.</p>
<p>Individuals are mutable, too. People age, change careers, lose interest, get sick, and ultimately die. It&rsquo;s okay to be mutable.</p>
<p>Even when we commit to a contractual obligation, the meaning of the contract can still vary within the interpretation of the law. Legal experts can sometimes help us to better understand all of the possible outcomes and how likely they are.</p>
<p>When we add a trust assumption in a specific protocol or a theorem, we can be confident that the nature of that trust is not going to change. Our understanding of it might change&ndash;perhaps we discover an undesirable property that was encoded within it. No worries, we can stop using the old undesirable protocol and switch to a new one.</p>
<p>Imagine a terrible law that was written before we knew any better, that is ultimately challenged and abandoned. The old immutable law still says what it said before, but our legal system decides to stop using it. Being immutable is okay, too.</p>
<h2 id="explicit-and-implicit">Explicit and Implicit</h2>
<p>To trust something explicitly is to fully consent to how it can behave and how it can change. To trust a complex system with many components of varying mutability as a single opaque agent is to accept its complexity implicitly.</p>
<p>When a couple starts dating, they start out trusting that their relationship is operating within the default expectations of our society or their social group. Sometimes that&rsquo;s as far as relationships get, they rely entirely on these implicit assumptions. Other relationships will slowly and explicitly map out important assumptions and boundaries: Are we exclusive? Have we been tested for STI&rsquo;s recently? What&rsquo;s our safe word? Are we always putting the toilet seat down?</p>
<p>Relationships start being based on implicit trust, but can become increasingly anchored by explicit trust.</p>
<p>When the CEO of a company makes a promise, they&rsquo;re making a commitment on behalf of all of the participants of the organization: The employees, the board, the suppliers and contractors, sometimes even the shareholders. How many times has a company promised us that our data is safe, only to leak their data a few months later because an employee was careless?</p>
<p>When we choose to trust a company to behave in some way, we are implicitly consenting to the behaviour of all of the components within that system.</p>
<p>Some trust assumptions are simple and explicit (&ldquo;I trust you to always put the toilet seat down&rdquo;) and we can have clear expectations of what happens if that trust is violated (toilet seat stays up). Other trust assumptions are just the tip of a complex iceberg of assumptions (&ldquo;I trust Apple to care about my privacy&rdquo;) and it can be extremely hard to enumerate all of the possible failure modes and fully consent, so we can choose to trust them implicitly.</p>
<p>Let&rsquo;s say that implicit trust assumptions are ones that contain an arrangement of additional trust assumptions behind it, with varying mutability and implicitness.</p>
<h2 id="trust-chains">Trust Chains</h2>
<p>The challenge with mutable and implicit trust points is that it&rsquo;s impossible to chain them while maintaining strong confidence. The complexity of assumptions spirals astronomically with each additional chain. Sometimes the legal system can help us mitigate this by enforcing assumptions from the outside: If we have a contractual agreement with all participating parties, then anyone deviating from the expectations can be later sued and perhaps damages can be recovered (but even that is merely a mitigation, the original desired state cannot be guaranteed).</p>
<p>What if we had a bunch of mechanisms that had explicit and immutable trust properties? We could chain them to arbitrary arrangements, while still having full confidence of the outcome&rsquo;s variability.</p>
<p>We can take several mathematical axioms, and use them to build complex mathematical theorems that maintain the same hard trust expectations we established from the axioms.</p>
<h2 id="deep--complex-relationships">Deep &amp; Complex Relationships</h2>
<p>In a world with more explicit and immutable trust mechanisms, we can create more interesting and complicated trusted relationships within our society.</p>
<p><strong>Trustless does not mean we must trust each other less, but rather it means that we can trust each other more!</strong></p>
<p>Even in our intimate relationships, ample communication and setting explicit boundaries allows us to create stronger and more complex intimate relationships. Relationships that are entirely bootstrapped from the implicit assumptions of our society&rsquo;s defaults are the ones that are limited within that same framework&ndash;to the point that we&rsquo;ve had to build an institution of marriage to enforce it with the help of our legal system.</p>
<p>With more explicit and immutable trust mechanisms, we can create more complicated cooperative collectives, we can create more interesting governance structures, we can create a larger solution space for our future.</p>
<p>Let&rsquo;s create a world with more better trust.</p>

      ]]></content:encoded></item><item><title>Decentralization</title><link>https://shazow.net/posts/decentralization/</link><pubDate>Mon, 27 Dec 2021 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/decentralization/</guid><description>&lt;p>As technological advancements march on, we&amp;rsquo;re having a lot of very important conversations about what we want out of the platforms we use day to day. We often talk about &amp;ldquo;decentralization&amp;rdquo; since it carries so much meaning, but it&amp;rsquo;s also easy to talk past each other because we may be thinking about different components of decentralization in different contexts.&lt;/p>
&lt;p>If we have two systems where one is more trustless and the second is more permissionless, can we say that one is decentralized but the other is not? Can we even say that one is &lt;em>more&lt;/em> decentralized than the other? Or does each simply have different points of centralization across different dimensions?&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>As technological advancements march on, we&rsquo;re having a lot of very important conversations about what we want out of the platforms we use day to day. We often talk about &ldquo;decentralization&rdquo; since it carries so much meaning, but it&rsquo;s also easy to talk past each other because we may be thinking about different components of decentralization in different contexts.</p>
<p>If we have two systems where one is more trustless and the second is more permissionless, can we say that one is decentralized but the other is not? Can we even say that one is <em>more</em> decentralized than the other? Or does each simply have different points of centralization across different dimensions?</p>
<p>Perhaps we need a clearer vocabulary to use when we discuss decentralization. Peer-to-peer, trustless, permissionless, non-custodial, credibly-neutral&hellip; What do we mean by these words?</p>
<h2 id="vocabulary">Vocabulary</h2>
<p>Let&rsquo;s break down some of the properties that are found in some of our fantasy decentralized systems, and imagine that each of these properties exists on a scale of 0 to 10:</p>
<ul>
<li><strong>Peer-to-peer:</strong> We can participate in the system by connecting directly with another peer, without going through a designated intermediary.</li>
<li><strong>Trustless:</strong> We can independently verify that all of the components of the system are behaving correctly.</li>
<li><strong>Permissionless:</strong> We can be equal participants in the system without requesting access from another entity that can act as a gatekeeper. This is also related to preventing censorship.</li>
<li><strong>Non-custodial:</strong> We can participate in the system without giving up or delegating any control (or power of outcome) to a provider.</li>
<li><strong>Credibly-neutral:</strong> The system is not systemically biased towards a specific sub-class of participants.</li>
<li><strong>Legitimate:</strong> The system is behaving in accordance to the rules that we collectively decided are desirable.</li>
<li><strong>Open Source</strong>: Anyone can read the source code of a system, and fork the development of the software if there is disagreement with its development.</li>
</ul>
<p>This is not a comprehensive list, but it helps to discuss concrete properties that fall under this umbrella.</p>
<p><strong>We must be mindful that changing the <em>scale</em> of one of these properties does not make a system entirely centralized or decentralized!</strong> By changing the design of our system, we can add or remove points of centralization across specific dimensions.</p>
<h2 id="why-are-these-properties-important">Why are these properties important?</h2>
<p>We can acknowledge the obvious reasons: When we give disproportional power to specific individuals, it can lead to abuse of that power.</p>
<p>But there is a more interesting property:</p>
<p><strong>It&rsquo;s easy to build centralized systems on top of decentralized systems, but it&rsquo;s hard to build decentralized systems on top of centralized systems.</strong></p>
<p>This asymmetry means that there is a larger possible <em>solution space</em> in decentralized systems than in centralized systems. One is a superset of the other.</p>
<p>Let&rsquo;s explore each component within this context:</p>
<ul>
<li><strong>Peer-to-peer:</strong> We can build a client/server relationship on top of peer-to-peer systems, but we can&rsquo;t directly connect to a peer if we&rsquo;re only allowed to connect through a designated server.</li>
<li><strong>Trustless:</strong> We can write a verifiable smart contract that lets an opaque box unilaterally control it. We can&rsquo;t build a verifiable smart contract on top of a opaque box.</li>
<li><strong>Permissionless:</strong> We can deploy a smart contract on a permissionless system that controls who can interact with it.</li>
<li><strong>Non-custodial:</strong> We can always delegate our secret private key to a custodian, like a spouse or a bank.</li>
<li><strong>Credibly-neutral:</strong> We can create a smart contract on a neutral system that gives preferential treatment to public keys that are on a special list.</li>
<li><strong>Legitimate:</strong> We can create a smart contract on a legitimate system which advertises one desirable behaviour, such as &ldquo;price always goes up&rdquo;, but ultimately exhibits an undesirable behaviour, such as &ldquo;creator stole all the money.&rdquo;</li>
<li><strong>Open Source</strong>: We can deploy a closed-source service that uses open source underneath. In fact, almost all closed-source software today relies on open source. Not hard to imagine.</li>
</ul>
<p>This is similar to how we can easily build an insecure system within a secure system, but it&rsquo;s extremely hard to build a secure system within an insecure system. Secure systems have a larger and more interesting solution space within them.</p>
<p>It&rsquo;s clear that we can take something completely decentralized in every respect, and easily build on top of it something that defeats every desirable property. Many &ldquo;scams&rdquo; take this shape: They exploit the intuition that a system with desirable properties would only be a host to other things with desirable properties.</p>
<h2 id="turtles-all-the-way-down">Turtles all the way down</h2>
<p>Our lives are composed of a mishmash of systems that are centralized or decentralized in different ways.</p>
<p>It can be convenient to point at one piece like TCP/IP exclaim &ldquo;the internet is decentralized!&rdquo; Or we can point at ICANN (or Amazon AWS or Facebook) and exclaim &ldquo;the internet is centralized!&rdquo;</p>
<p>When we realize how it&rsquo;s easy to build centralized systems on top of decentralized systems, it&rsquo;s unsurprising that we can find so many conflicting examples.</p>
<p>In many ways, our political systems fall on these axes, too: We value <em>legitimate</em> democracies, and eschew corrupted authoritarianism. We fear that people in positions of power will abuse them without &ldquo;transparency&rdquo; and &ldquo;checks and balances&rdquo;. We want to have private and personal relationships with people. We like the idea of living in a society that allows us to do creative things without asking for permission, and we would prefer a future where a small class of people doesn&rsquo;t have disproportional power and wealth over everyone else.</p>
<p>Other interesting examples to contemplate within this framework: In what properties and scales is our legal system decentralized? What about realestate ownership? Modern medicine?</p>
<h2 id="what-does-decentralization-solve">What does decentralization solve?</h2>
<p>Ask not what decentralization can do for you, but what you can do for decentralization.</p>
<p>Decentralization can remove the surface area for problems that are <em>created</em> by centralization, such as those caused by added intermediaries and custodians.</p>
<p>Let&rsquo;s setup a fantasy strawman scenario to illustrate this:</p>
<p>Imagine Twitter starts requiring a new kind of intermediary for posting on their service: Moving forward, everyone has to tweet through another Twitter employee. That person will get your username and password, and you&rsquo;ll need to phone them up and dictate your tweet for them to post on your behalf. What are some things that can go wrong here?</p>
<p>For one, we&rsquo;re going to have all kinds of communication issues&ndash;literally a game of broken telephone! Tweets are going to get miswritten due to bad phone reception, or misunderstanding of accents, or the intermediary having a bad day, or maybe they&rsquo;re working with a political figure that they despise. We&rsquo;ll need to hire thousands or maybe millions of intermediaries to do this job, and this also opens up an opportunity for the intermediary to leak our password, or outright hijack our account and post unauthorized tweets. Perhaps we can discourage it with a licensing scheme and harsh penalties.</p>
<p>Admittedly Twitter was not very decentralized to begin with, but let&rsquo;s work through our framework:</p>
<ul>
<li>Peer-to-peer? We now have <em>another</em> designated intermediary that we&rsquo;re required to use in the chain, so this got worse.</li>
<li>Trustless? Another intermediary in the trust chain that we can&rsquo;t audit, this got worse.</li>
<li>Permissionless? Even if we made it through Twitter&rsquo;s original gatekeeping, the new intermediary can decide to block us.</li>
<li>Non-custodial? This new intermediary has our username and password, and they can do what they want with it. We&rsquo;ve given up all control over our Twitter accounts to this custodian.</li>
<li>Credibly-neutral? Perhaps the licensing scheme and penalties could keep most of these intermediaries in line, but ultimately that&rsquo;s one more point where bias can be exercised to dis-empower classes of people. Even politics aside, imagine using this phone system with a speech impediment. If Twitter was 6/10 on this property before, it&rsquo;s more like 4/10 now.</li>
<li>Legitimate? This one is more interesting here. If we as a society decide that these intermediaries are ultimately doing a great service to us by exercising their judgement and reducing content that we don&rsquo;t want anyway&hellip; it&rsquo;s possible that this fantasy could end up <em>more</em> legitimate than original Twitter?</li>
<li>Open source? Since this is already a closed system, this reduces to the Trustless property. One more opaque step that we can&rsquo;t audit.</li>
</ul>
<p>Now imagine we remove this added intermediary, and suddenly all of these new problems disappear from that particular point. Other points of centralization still exist as they did before, and perhaps we can work towards removing them too someday.</p>
<p>Clearly this is not a serious example, but it illustrates important points:</p>
<ol>
<li><strong>We can always add more intermediaries</strong>, it&rsquo;s very easy! This changes the decentralization properties of our system.</li>
<li><strong>We can remove intermediaries</strong>, but it&rsquo;s harder as they become integrated into our institutions. Imagine if it&rsquo;s wildly profitable to be a Twitter Intermediary due to bribes, or the contrary: the thousands of underpaid and overworked employees form a union that becomes too difficult to bargain against. How can we conscionably take away jobs from so many people?</li>
<li>Since they&rsquo;re so easy to add, intermediaries exist everywhere. How do we evaluate which ones are worth doing the hard work of removing? Can we even anticipate what the outcomes will be?</li>
</ol>
<p>This is again similar to security: Removing components that don&rsquo;t have the necessary guarantees is the best way to improve security, but it can be hard to remove components without sacrificing their features that we&rsquo;ve become dependent on. On the other hand, how many times have we found fragile dependencies in our systems that we don&rsquo;t need anymore?</p>
<h2 id="decentralization-is-not-a-panacea">Decentralization is not a panacea</h2>
<p>We have many challenges in modern society that are orthogonal to whether they&rsquo;re expressed by a centralized system or decentralized system.</p>
<p>For example, consider ownership: We can have a centralized physical ledger on our Mayor&rsquo;s desk, with entries of who owns what. Or we can have a decentralized digital ledger, with the very same entries. In either case, it does not change what policy on ownership we enact as a society. It does not change what taxes we choose to collect, or what kind of construction zoning we allow, or if we decide to disregard that ledger and start anew.</p>
<p>Some other social challenges worth noting: <strong>Decentralization does not solve wealth inequality</strong>, it does not solve local law enforcement, it does not solve the ability for people to create centralized systems within it (and all of the problems that are created from them).</p>
<h2 id="when-is-centralization-useful">When is centralization useful?</h2>
<p>Most properties of centralization are about concentrating control or power in a specific component, this can make it very easy to make quick unilateral changes in policy. To decentralize is to give up power or control.</p>
<p>When we imagined the fantasy Twitter scenario, the added intermediaries acquired a powerful position: They gained control of what tweets were being posted. To remove them, they would need to give up that power.</p>
<p>We can imagine scenarios where having ultimate power and control can be vital, such as a military chain of command. When facing dire threat and every moment is a life-or-death decision, having competent leadership with full control over their domain is crucial.</p>
<p>Centralized properties can be very convenient and powerful, and some of their harm can be mitigated by placing them within structured decentralized process&ndash;like giving a representative partial control over an organization, while still being able to vote them out. More harmful parts of centralization are amplified when they approach permanent capture, when we become too dependent on them and we&rsquo;re no longer able to exercise any mitigations for misbehaviour.</p>
<p>It is up to us to choose whether we allow more and more parts of our lives to become captured in centralization, or if we want to pursue the dream of decentralizing things that were not possible to decentralize before and see where that takes us.</p>
<hr>
<p><a href="https://github.com/shazow/shazow.net/issues/41#issuecomment-1000555663"><em>Appendix with additional notes and links</em></a>.</p>
<p><em>Thanks for feedback on early drafts to Benjamin, Ezzeri, Harper, Jenny, Max, Phill.</em></p>

      ]]></content:encoded></item><item><title>How does DeFi yield?</title><link>https://shazow.net/posts/ethereum-defi-yield/</link><pubDate>Wed, 07 Apr 2021 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/ethereum-defi-yield/</guid><description>&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Decentralized_finance">Decentralized Finance&lt;/a> (DeFi) is a technology that exploded in popularity in 2020, taking place &lt;a href="https://defipulse.com/">almost entirely on the Ethereum network&lt;/a>.&lt;/p>
&lt;p>There are many things about it that are outright fascinating: Algorithmic stablecoins, automated market makers, flash loans, flash &lt;em>mints!&lt;/em> Every month there is a wild new invention that changes the landscape, often increasingly difficult to understand which is both frustrating and intimidating.&lt;/p>
&lt;p>I want to talk about one thin slice of this technology that I&amp;rsquo;m seeing a lot of people have trouble grasping: &lt;strong>Where does the yield come from?&lt;/strong>&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p><a href="https://en.wikipedia.org/wiki/Decentralized_finance">Decentralized Finance</a> (DeFi) is a technology that exploded in popularity in 2020, taking place <a href="https://defipulse.com/">almost entirely on the Ethereum network</a>.</p>
<p>There are many things about it that are outright fascinating: Algorithmic stablecoins, automated market makers, flash loans, flash <em>mints!</em> Every month there is a wild new invention that changes the landscape, often increasingly difficult to understand which is both frustrating and intimidating.</p>
<p>I want to talk about one thin slice of this technology that I&rsquo;m seeing a lot of people have trouble grasping: <strong>Where does the yield come from?</strong></p>
<p>A big part of the DeFi movement is easy access to pools of high-yielding assets. The highest-yielding assets are leveraged derivatives of other liquidity pools, but at the foundation we have access to an investment vehicle that has impressive returns with <em>zero economic risk</em>. How is this possible?</p>
<p>Traditionally, returns on investments come from inflation, inefficiency, speculation/risk, or violation of trust.</p>
<p>We have government bonds that push the interest rate floor that private institutions offer. We have arbitrage opportunities which increase liquidity between markets. We have investors making claims that something is going to be worth more or less in the future, arguably contributing to price discovery. We have institutions giving out debt in hopes that they&rsquo;ll get it back with some interest. We have ponzi schemes where unscrupulous fund custodians provide some clients returns taken directly from the reserves of other clients until it all falls apart.</p>
<h2 id="how-is-defi-different">How is DeFi different?</h2>
<p>Sure, all the same old tricks exist. There&rsquo;s no shortage of scams, there&rsquo;s volatility between markets, there&rsquo;s speculation, there&rsquo;s inflation and disinflation. None of this is new—no one is claiming it is.</p>
<p>Here is what&rsquo;s new:</p>
<ol>
<li>Non-custodial contracts (reducing the reliance on trusting intermediaries)</li>
<li>Algorithmic contracts (further reducing trust, introducing determinism, reducing inefficiency)</li>
<li>Atomic contracts: The ability to write a series of instructions with the guarantee that either all of it or none of it executes. This is key, and the consequences are most profound.</li>
</ol>
<h2 id="contract-atomicity">Contract Atomicity</h2>
<p>If you&rsquo;ve ever typed <code>BEGIN;</code> and <code>COMMIT;</code> then you&rsquo;re familiar with atomic transactions in databases.</p>
<p>ACID-compliant database transactions have been a thing since the 70s, but it&rsquo;s something that has never made it to the global financial system. A single institution might promise that a list of instructions sent to them would be executed as a single unit, but the reality is that anything that spans institutions will have multi-day settlement periods and often involve humans signing off on parts of it. Even within a single institution, operations are often batched and batches are executed periodically. Very little is actually <em>real time</em> in traditional finance.</p>
<p>Let&rsquo;s say we see two marketplaces which value apples differently. We can buy a bunch of apples from one, haul them over to the other market, sell them for a profit. That sounds great in theory, but by the time we arrange the purchase from one market, load them onto our cart, and haul them over to the other market, a few thousand high-frequency trading bots from Wall Street have moved the price of apples a million times and we&rsquo;re probably not going to make minimum wage on our effort.</p>
<p>To make it worth our while, the difference has to be substantial and our capital has to be huge to really take advantage of it. This is why arbitrage hedge funds are typically managing upwards of hundreds of millions of dollars in assets, and a single avid <a href="http://reddit.com/r/wallstreetbets">WSB</a> reader wouldn&rsquo;t dare compete&hellip; Usually.</p>
<p>Imagine we could pull out cash from our bank, buy apples, move apples, sell apples, and get cash back into our back account in a single atomic transaction. Either the whole thing succeeds, or none of it happens. It&rsquo;s <em>zero risk</em> arbitrage (though there are fixed fees for the privilege).</p>
<p>That&rsquo;s great! We can complete with high frequency traders now, but we still can&rsquo;t compete with their capital.</p>
<p>But wait, what else can we do with atomic transactions?</p>
<p>Imagine we could take out a loan for a million dollars, buy <em>a lot</em> of apples, sell the apples, pay back the loan plus a fee, and keep the difference in a single atomic transaction. Either the whole thing succeeds, or none of it happens. It&rsquo;s <em>zero risk</em> loans for <em>zero time</em> (except we pay a fee to the liquidity pools who lock their capital that we loan from). This is called <a href="https://medium.com/monolith/understanding-defi-flash-loans-explained-1a5928a4a612">flash loans</a>.</p>
<p>Now we can compete with wealthy hedge funds, as long as we can afford the modest flash loan fees (these vary, but roughly 0.0X% in fees) and transaction gas fees.</p>
<p>There are other <em>even wilder</em> possibilities with atomic transactions, like flash mints (instead of loan money, create money for zero time), but let&rsquo;s talk about zero time.</p>
<h2 id="zero-time">Zero Time</h2>
<p>The longer a debt is held, the more unknowns are added to the probability that the debt will be paid out. A mortgage holder could lose their job, a bank could buy debt that represents a bunch of people who lost their jobs, a global pandemic could ravage our local economy. If my friend wants twenty bucks for a second, I&rsquo;m not too worried about not getting it back unless they&rsquo;re <a href="https://www.youtube.com/watch?v=eYVEvQKfcpM">an illusionist</a>. If my friend wants to borrow twenty bucks for ten years, I doubt I&rsquo;ll see it again. So much can change with time, I&rsquo;ll probably forget about it.</p>
<p>A flash loan does not exist outside of the scope of the transaction. In effect, a flash loan is debt that exists for precisely zero time. Aside from the real-but-tractable likelihood that there is a software bug in the system, there is no risk of an atomic zero-time loan not being paid back.</p>
<p>The idea is sound, even if there have been times when a particular implementation was not. It seems that only people who have trouble grokking zero-time loans are expecting software to be perfect in zero-time. 🙃</p>
<p>In fact, <a href="https://coingeek.com/the-defi-hacks-of-2020/">DeFi exploits are some of the most fascinating users of flash loans</a>, since they&rsquo;re able to amplify market imbalances in unexpected ways with incredible effect. These are not fundamental flaws, and they&rsquo;ve all been mitigated in modern DeFi contracts, but I&rsquo;m sure there will be more before things become completely safe and stable.</p>
<p>That&rsquo;s the big difference with traditional finance: Even after eons of custodial trust violations, from the very first primate to borrow a tool and not return it as promised, we are still struggling with the same fundamental shortcomings. Meanwhile, DeFi on Ethereum has only been alive for a couple of years, and there is a very achievable end point of stability and safety.</p>
<h2 id="faqs">FAQs</h2>
<blockquote>
<p>Is this investment advice? 😳🫴🦋</p>
</blockquote>
<p>No.</p>
<blockquote>
<p>Could we implement this on traditional finance infrastructure?</p>
</blockquote>
<p>It&rsquo;s possible. Meanwhile, we&rsquo;re still seeing banks sweating to maintain <a href="https://www.howtogeek.com/667596/what-is-cobol-and-why-do-so-many-institutions-rely-on-it/">billions of lines of COBOL running in production</a> that was written <em>50+ years ago</em>. Ask again in a hundred years?</p>
<p>Even if we could wave a magic wand and retrofit all of the world&rsquo;s banking today, the custodial nature of traditional banking is what introduces a lot of the risk. Can a loan across multi-national banking institutions truly be risk-free if each institution has to <em>trust</em> the other side to keep up its end of the deal? Once we move to a trustless non-custodial system that has Turing-complete atomic instructions, we end up where we are today with Ethereum&rsquo;s DeFi ecosystem.</p>
<blockquote>
<p>No risk is never <em>truly</em> no risk!</p>
</blockquote>
<p>Of course. Software bugs happen, human error happens, black swan events happen. The goal of these mechanisms is to dramatically reduce the number of intermediaries and cut down on the surface area for risk, in ways we haven&rsquo;t been able to do before.</p>
<blockquote>
<p>Huh, free money with no risk? Wouldn&rsquo;t everyone put their money in this and the returns go to 0?</p>
</blockquote>
<p>That&rsquo;s not impossible! But keep in mind, flash loans is just one of a multitude of yield-earning mechanisms, and one with the lowest returns already. There are plenty of other mechanisms that pay far more but involve more hands-on babysitting (like adjusting collateral ratios as prices change) or have more risk (less mature smart contracts that could be exploitable) or are more speculative. Furthermore, as more money enters the ecosystem, more opportunity becomes available for flash-loan trading bots to do their thing.</p>
<blockquote>
<p>How much yield could I get right now? Asking for a friend&hellip;</p>
</blockquote>
<p>Depends on your risk tolerance and how hands-on you want to be. Many of these rates vary wildly from week to wee.</p>
<blockquote>
<p>What are flash loan bots <em>actually</em> doing?</p>
</blockquote>
<p>Here&rsquo;s a well-documented smart contract bot that takes out a loan from Aave and performs arbitrage between two markets on ETH-DAI: <a href="https://github.com/fifikobayashi/Flash-Arb-Trader">https://github.com/fifikobayashi/Flash-Arb-Trader</a></p>
<p>More sophisticated bots in production today traverse much more complex paths across many lenders, exchanges, and trading pairs before completing a profitable cycle. All atomically, of course.</p>
<hr>
<ul>
<li>Special thanks to Tracy, Charlie, Phill, and others for feedback.</li>
</ul>

      ]]></content:encoded></item><item><title>I tried the Oculus Quest 2 for a couple of hours and wrote a review</title><link>https://shazow.net/posts/oculus-quest-2-review/</link><pubDate>Fri, 04 Dec 2020 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/oculus-quest-2-review/</guid><description>&lt;p>For context, this is comparing with my Valve Index which is not very apples-to-apples.&lt;/p>
&lt;p>This is specific to the wireless embedded mode, not tethered Quest Link mode. I tested it with the Elite Strap, which ought to be included by default.&lt;/p>
&lt;h2 id="tracking">Tracking&lt;/h2>
&lt;p>Tracking is &amp;ldquo;surprisingly good&amp;rdquo; as everyone says, especially superficially. It&amp;rsquo;s hard to visually detect glitches when the system is operating smoothly.&lt;/p>
&lt;p>On the other hand, I did feel mild nausea after some casual use. Normally I can use my Index (or old Vive) for hours without nausea. It&amp;rsquo;s hard to tell if this is due to some more subtle tracking issues, or because of the limited lens controls, or purely from processing glitches.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>For context, this is comparing with my Valve Index which is not very apples-to-apples.</p>
<p>This is specific to the wireless embedded mode, not tethered Quest Link mode. I tested it with the Elite Strap, which ought to be included by default.</p>
<h2 id="tracking">Tracking</h2>
<p>Tracking is &ldquo;surprisingly good&rdquo; as everyone says, especially superficially. It&rsquo;s hard to visually detect glitches when the system is operating smoothly.</p>
<p>On the other hand, I did feel mild nausea after some casual use. Normally I can use my Index (or old Vive) for hours without nausea. It&rsquo;s hard to tell if this is due to some more subtle tracking issues, or because of the limited lens controls, or purely from processing glitches.</p>
<p>It&rsquo;s not hard to make the embedded computing system overload and jitter. Using it while apps are installing causes tracking failures, or using insufficiently &ldquo;optimized&rdquo; apps like VR Chat. The tracking will freeze occasionally or swing aggressively.</p>
<p>It&rsquo;s not very common under more &ldquo;Made for Quest&rdquo; experiences.</p>
<p>I was particularly concerned about the Quest inside-out controller tracking but again I was surprised at how well they worked. Not perfect (there were positions where they jittered or disappeared), but better than expected. Under good operating conditions, it was more than tolerable.</p>
<h2 id="display">Display</h2>
<p>Text clarity is <em>very</em> very good, but the graphics fidelity on everything else is noticeably sad.</p>
<p>Granted it&rsquo;s an underpowered platform, compared to an expensive gaming computer, but this comes with consequences.</p>
<p>Obviously &ldquo;optimized&rdquo; scenes are very low-polygon, but that can still work under the right context. The thing that is more noticeable is the textures are very low quality too. The end result feels kind of like playing a game on Low Quality settings in 800x600 resolution but on a 6K high-DPI display. The vector-rendered elements are very crisp, but everything else is from a different lo-fi universe.</p>
<p>The field of view is noticeably tighter than the Valve Index, but I am also personally less bothered by this than most.</p>
<p>No complaints with any screen door effect or godrays.</p>
<h2 id="wireless">Wireless</h2>
<p>It is very pleasant to be wireless. Wired headsets never bothered me as much as it bothers some people, but I do wish the Valve Index came out with a wireless module already. At minimum, it makes the &ldquo;grab and play&rdquo; experience a bit better.</p>
<p>On the other hand, I suspect the Quest 2 is a <em>much</em> better device when tethered to a gaming box via Quest Link. I haven&rsquo;t tried it in this mode, since it&rsquo;s hard to imagine it being better than the Index in any metric so why bother&hellip; But if it&rsquo;s a question of budget, then it seems like a viable option.</p>
<h2 id="audio">Audio</h2>
<p>While wearing the Quest 2, the built-in audio is fine. Not nearly as good as the Index, which is better than a lot of dedicated headphones!</p>
<p>On the other hand, the sound is <em>more</em> audible outside of the headset than inside. Anyone else in the room gets to experience everything you experience at x1.25 the volume.</p>
<p>One of the cool parts of audio inside of a head-tracked VR environment is that it&rsquo;s easy to simulate 3D audio very well, this is great for the wearer but terrible for everyone else. I could feel a bit of nausea just sitting in the same room as someone playing the Oculus Quest 2, just from all of the simulated audio positioning shifting.</p>
<p>Thankfully there is an AUX port on the headset so it&rsquo;s easy to put on your own headphones.</p>
<h2 id="comfort">Comfort</h2>
<p>There are only two IDP presets, luckily one was pretty close to my head shape. My eyelashes hit the lens a bit, and luckily I do not need to wear glasses.</p>
<p>With the Elite Strap, the device is a good weight and comfortable to wear for extended periods, no complaints. Not quite as comfortable and adjustable as the Valve Index, but a fair bit lighter. If the Valve Index is a Herman Miller Aeron Chair, the Oculus Quest 2 with Elite Strap is a budget look-alike from Staples that gets you most of the way there.</p>
<p>Certainly more than good enough to sit back and enjoy a full-feature 3D film, or dance around for a couple hours.</p>
<h2 id="controllers">Controllers</h2>
<p>No complaints but they do have a &ldquo;budget&rdquo; feel to them coming from the Index Knuckles, or even the Vive Wands. They&rsquo;re small, but reasonably comfortable. Not certain they would win in a fight against a TV, or certainly not against some drywall (which my old Vive wands have more than enjoyed taking on).</p>
<h2 id="oculus-store">Oculus Store</h2>
<p>Compared to Steam, the Oculus Store is a sad experience. It was difficult to find a hierarchical listing of games or experiences. The search worked well, but you need to know what to search for. Overall it did not feel like the people who designed it live on the same planet as I do.</p>
<h2 id="facebook">Facebook</h2>
<p>The device and all of your purchases are locked to your Facebook account. Fuck Facebook. This is a deal breaker for me and should be for everyone, I would never spend my own money on anything that profits Facebook. My only hope is that they&rsquo;re selling the Quest 2 below cost and that it&rsquo;s thoroughly jailbroken soon.</p>
<h2 id="overall">Overall</h2>
<p>I really wish there was another competitor in the &ldquo;embedded wireless with optional tethered&rdquo; tier that I could recommend as an alternative, but there isn&rsquo;t. For people who can&rsquo;t commit to a gaming box yet, there&rsquo;s simply no other option. I can&rsquo;t fault Facebook for cannibalizing and betraying the Rift team with the Quest line, it&rsquo;s clearly the best move in our current VR ecosystem, especially at the price point they&rsquo;re selling.</p>
<p>Overall it feels like a $500-600 device being sold for $370 USD (with the Elite Strap), with clear intent and inevitable success of dominating the entry-level tier of VR. I&rsquo;m sure Facebook will more than make up any losses through the walled-garden they&rsquo;ve assembled.</p>

      ]]></content:encoded></item><item><title>Using Github Issues as a Hugo frontend with Github Actions and Netlify</title><link>https://shazow.net/posts/github-issues-as-a-hugo-frontend/</link><pubDate>Tue, 01 Dec 2020 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/github-issues-as-a-hugo-frontend/</guid><description>&lt;p>I got into the habit of dumping quick blog post ideas into issues on my blog&amp;rsquo;s repo. It&amp;rsquo;s a convenient place to iterate on them and share with friends for feedback before actually publishing on my blog post.&lt;/p>
&lt;p>&lt;img src="100761218-a6cb2280-33c0-11eb-92df-1b52d91cc16e.png" alt="image">&lt;/p>
&lt;p>The drafts keep accumulating, how do I trick myself into publishing more? Perhaps by reducing the effort required for the next step? Let&amp;rsquo;s do it!&lt;/p>
&lt;h2 id="architecture">Architecture&lt;/h2>
&lt;p>My blog is statically generated using &lt;a href="https://github.com/gohugoio/hugo">Hugo&lt;/a>, the &lt;a href="https://github.com/shazow/shazow.net">code is hosted on Github&lt;/a>, then when a pull request comes in it is built, previewed, and published on merge by &lt;a href="https://netlify.com/">Netlify&lt;/a>.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>I got into the habit of dumping quick blog post ideas into issues on my blog&rsquo;s repo. It&rsquo;s a convenient place to iterate on them and share with friends for feedback before actually publishing on my blog post.</p>
<p><img src="100761218-a6cb2280-33c0-11eb-92df-1b52d91cc16e.png" alt="image"></p>
<p>The drafts keep accumulating, how do I trick myself into publishing more? Perhaps by reducing the effort required for the next step? Let&rsquo;s do it!</p>
<h2 id="architecture">Architecture</h2>
<p>My blog is statically generated using <a href="https://github.com/gohugoio/hugo">Hugo</a>, the <a href="https://github.com/shazow/shazow.net">code is hosted on Github</a>, then when a pull request comes in it is built, previewed, and published on merge by <a href="https://netlify.com/">Netlify</a>.</p>
<p>The blog post drafts are posted as Github issues, so there is a clear gap: How do we convert issues into pull requests for Netlify? Enter Github Actions!</p>
<h2 id="github-action-issue-to-pull-request">Github Action: Issue to Pull Request</h2>
<p>My <a href="https://github.com/shazow/shazow.net/blob/master/.github/workflows/publish.yml">full workflow lives here</a> if we want to jump ahead, but let&rsquo;s break down the broad strokes.</p>
<p>I decided to trigger the publishing process once an issue is labelled with &lsquo;publish&rsquo;, so let&rsquo;s start with that:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Publish post from issue</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">issues</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">types</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s1">&#39;labeled&#39;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">${{ github.event.label.name == &#39;publish&#39; }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="l">...</span><span class="w">
</span></span></span></code></pre></div><p>Next up we want to specify the steps, first thing is to check out the repository into the action&rsquo;s environment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v2</span><span class="w">
</span></span></span></code></pre></div><p>Once the source code is available, we want to generate the blog post from the issue metadata. Here is a very basic version of this, though I ended up doing more tweaking in the end:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Generate Post</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">POST_TITLE</span><span class="p">:</span><span class="w"> </span><span class="l">${{ github.event.issue.title }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">POST_BODY</span><span class="p">:</span><span class="w"> </span><span class="l">${{ github.event.issue.body }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          cat &gt; &#34;content/posts/${POST_TITLE}.md&#34; &lt;&lt; EOF
</span></span></span><span class="line"><span class="cl"><span class="sd">          ${POST_BODY}
</span></span></span><span class="line"><span class="cl"><span class="sd">          EOF</span><span class="w">          
</span></span></span></code></pre></div><p>This shoves the body of the issue, which is already markdown, into a markdown file named based on the title of the issue. This is a good place to add frontmatter, or slugify the title, or whatever else your blog setup requires.</p>
<p>Running the payload through environment variables helps with not needing to escape various characters like `.</p>
<p>And finally, we make the pull request using Peter Evan&rsquo;s create-pull-request action which makes this super easy:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create Pull Request</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peter-evans/create-pull-request@v3</span><span class="w">
</span></span></span></code></pre></div><p>This is the minimum of what we need, but we can specify all kinds of additional options here: like auto-deleting the branch, setting a custom title, body, and whatever else. Here&rsquo;s an example of what I&rsquo;m doing:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Create Pull Request</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">peter-evans/create-pull-request@v3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">delete-branch</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;publish: ${{ github.event.issue.title}}&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">body</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">            Automagically sprouted for publishing.
</span></span></span><span class="line"><span class="cl"><span class="sd">            Merging will publish to: https://shazow.net/posts/${{ github.event.issue.title }}
</span></span></span><span class="line"><span class="cl"><span class="sd">            Closes #${{ github.event.issue.number }}</span><span class="w">            
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">reviewers</span><span class="p">:</span><span class="w"> </span><span class="l">${{ github.repository_owner }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">commit-message</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;post: ${{ github.event.issue.title }}&#34;</span><span class="w">
</span></span></span></code></pre></div><h2 id="result">Result</h2>
<p>When my blog post draft is ready, I add the tag and the Github action takes it away, creating a pull request:</p>
<p><img src="100763017-a764b880-33c2-11eb-860f-5bab932ac558.png" alt="image"></p>
<p>The pull request automatically pings me as a reviewer, and includes a &ldquo;Closes #X&rdquo; line which will close the draft issue once the PR is merged. Very convenient!</p>
<p><img src="100763219-ded36500-33c2-11eb-8387-ff28b6561875.png" alt="image"></p>
<p>Once the pull request is ready, Netlify takes it away, builds everything and generates a handy preview:</p>
<p><img src="100763300-fa3e7000-33c2-11eb-9172-206f58556ddd.png" alt="image"></p>
<p>I can make sure everything looks right, and even apply edits directly inside the pull request. This is another great step to send a long blog post for feedback, using all of the wonderful Pull Request Review features!</p>
<p>When all is said and done, merging the pull request triggers Netlify to publish my changes to my domain, and merging closes the original issue, and I&rsquo;m done!</p>
<h2 id="bonus">Bonus</h2>
<p>Drag n&rsquo; drop images work in Github Issues, so it&rsquo;s super easy to write a quick post with a bunch of screenshots or what have you.</p>
<p>It&rsquo;s important to me that I&rsquo;m not too tightly coupled to third-party services, so the pull request and code merge flow makes sure that all of the published state continues to live inside of my Git repository.</p>
<p>I can still make blog posts the way I used to: Pull the latest repo, write some markdown, and push to publish.</p>
<p>I added a little <a href="https://github.com/shazow/shazow.net/blob/master/frontmatterify">frontmatterify script</a> to process the incoming markdown and convert the remote Github Issue uploaded images into local images that are included in the pull request. The script also generates frontmatter that I use for Hugo. It&rsquo;s a bit clunky but works for now.</p>
<p>Alright, let&rsquo;s do this.</p>
<p><img src="100764184-11ca2880-33c4-11eb-8c84-e992765ace49.png" alt="image"></p>

      ]]></content:encoded></item><item><title>Beginner Sourdough: Does anything really matter?</title><link>https://shazow.net/posts/beginner-sourdough-does-anything-really-matter/</link><pubDate>Wed, 22 Apr 2020 21:55:09 +0000</pubDate><guid>https://shazow.net/posts/beginner-sourdough-does-anything-really-matter/</guid><description>&lt;p>There are bazillions of sourdough recipes in the wild, foaming with traffic from eager bakers trapped in quarantine, ready for their sourdough starter baby with a punny name to graduate into a tube of sustenance and distraction during dark times.&lt;/p>
&lt;p>Sourdough recipes are more involved than regular bread recipes, that’s for sure. There are many steps, and bakers can’t seem to agree on many of them. That’s no accident, a great sourdough bake needs to be adjusted to our kitchen and our starter.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>There are bazillions of sourdough recipes in the wild, foaming with traffic from eager bakers trapped in quarantine, ready for their sourdough starter baby with a punny name to graduate into a tube of sustenance and distraction during dark times.</p>
<p>Sourdough recipes are more involved than regular bread recipes, that’s for sure. There are many steps, and bakers can’t seem to agree on many of them. That’s no accident, a great sourdough bake needs to be adjusted to our kitchen and our starter.</p>
<h2 id="what-to-expect-when-youre-expecting-sourdough">What to expect when you’re expecting Sourdough</h2>
<p><strong>This article is not a sourdough recipe or guide.</strong> It’s more of a guide for reading sourdough recipes. It’s everything I wish I knew after reading my first sourdough recipe. When we get that faithful link from our baker friend with the only caption saying “just do what it says,” read through it, take it in, then come back here for some added context. (For some decent recipes, check the links at the end of the article.)</p>
<p>Let’s break down a standard sourdough bread recipe, look at what varies between them, and how to adjust the recipe fit our kitchen.</p>
<p>A typical sourdough recipe comes down to four things:</p>
<ol>
<li><strong>Component ratio:</strong> How to adjust recipe measurements to fit the size loaf we want.</li>
<li><strong>Component ingredients:</strong> What are we using to make our sourdough? What kind of flour?</li>
<li><strong>Time, Temperature, and Yeast:</strong> This is what varies from baker to baker, and what we need to know to compensate in our steps.</li>
<li><strong>Steps:</strong> The bulk of the recipe, from crucial technique to flourishes and rituals.</li>
</ol>
<h2 id="component-ratio">Component Ratio</h2>
<p>Whether our recipe measures in grams or ounces or cups, the thing that really matters is the ratio between ingredients: Starter to Water to Flour.</p>
<p>For example, 50 grams of starter, 50 grams of water, and 100 grams of flour is 1:1:2 in ratio short-hand.</p>
<p>It can also be expressed as a <a href="https://en.wikipedia.org/wiki/Baker_percentage">Baker’s Percentage</a>: 100% flour, 50% starter, 50% water. When using Baker’s Percentages, everything is relative to the weight of the flour, so flour is always 100%.</p>
<p>Once we have the ratio, we can scale the recipe to whatever total quantity we’d like, whether it’s a small 300g bun or a big ol’ 1.5kg absolute unit.</p>
<p>The ratio also tells us the hydration of the bread (baker percentage of total water content). Higher hydration can make the bread’s crumb more open and fluffy, but it can also make the dough stickier and more difficult to work with. This is one of the common things that vary in recipes, whether explicitly or indirectly.</p>
<p>We can use a Bread Calculator to figure all of this out ahead of time, and tweak the components to meet our goal.</p>
<ul>
<li><a href="https://breadcalc.com/">https://breadcalc.com/</a></li>
<li><a href="http://brdclc.com/">http://brdclc.com/</a></li>
</ul>
<p>Tried at 60% hydration loaf last time but want to bring it up to 75% this time? Plug in our measurements and tweak the water or flour components until our hydration ratio where we want it.</p>
<p>Other components are usually expressed in baker’s percentages too, like 2% salt is standard.</p>
<h2 id="component-ingredients">Component Ingredients</h2>
<p>Okay, so we have our ratio of flour and water, but <em>what kind of flour?</em></p>
<p>Most beginner sourdough recipes recommend using Bread Flour. It has more protein than All Purpose Flour which gives the starter yeast more nutrition to work with.</p>
<p>There are many other kinds of flours out there, like Whole Wheat Flour and Rye Flour, which have even more protein. If all you have is All Purpose Flour and Whole Wheat or Rye, then considering mixing them to give your starter yeast more to work with. Too much Whole Wheat or Rye can be difficult to work with, so start with majority of your base flour (Bread Flour ideally, All Purpose otherwise). Maybe something like 5:1 base flour to other flour, and tweak from there.</p>
<p>Avoid flours designed to be low on protein, like pastry flour.</p>
<p>Once we’re ready to experiment more, we can toss in some herbs, cheese, seeds, the possibilities are endless!</p>
<h2 id="time-temperature-and-yeast">Time, Temperature, and Yeast</h2>
<p>Any sourdough bread recipe will have a bunch of steps with specific timelines they recommend. Half hour to rest, a few hours to bulk ferment, another period to proof. The problem with most recipes is that they’re written for the author’s specific kitchen and starter.</p>
<p>We’ll stumble on professional baker in one end of the world heatedly debating another baker on the other side, both with thousands of loafs under their belt but they just can’t agree on The Correct Way To Make Sourdough! “I’ve always proofed mine for no more than 3 hours and it comes out perfect every time,” one will insist. “No way, you need to proof for minimum 5 hours, until a flock of pigeons fly by the window,” the other will protest.</p>
<p>While I’m just someone who read too many recipes and discussion boards on the topic, here are some of my notes on what I discovered truly matters:</p>
<h3 id="ambient-temperature"><strong>Ambient Temperature</strong></h3>
<p>A cold kitchen will take a lot longer for a dough to rise than a warm kitchen. It can be the difference between a 3 hour bulk fermentation and an 8 hour. Or 10% sourdough starter and 30% sourdough starter. If we have a really warm kitchen, we can scale down the amount of starter we use to slow down the fermentation. A longer fermentation can yield a richer sour flavour, so we want that.</p>
<p>It will also take a lot longer for a new sourdough starter to mature in a cold kitchen, one week can become two weeks. It can help to create a warm space for the dough to rise more efficiently: An oven with the oven light on, or a warming mat typically used for seedlings. Be careful to not go too warm, the yeast starts to die at around 100F. 80–85F is the recommended range for a good efficient rise. <a href="https://www.theperfectloaf.com/the-importance-of-dough-temperature-in-baking/">Periodically checking the dough’s core temperature</a> is a good way to control this variable better.</p>
<h3 id="starter-and-yeast-activity"><strong>Starter and Yeast Activity</strong></h3>
<p>A less mature starter is less active, possibly not even ready to make bread with. If we used a low-protein flour for our starter, it can take longer to ramp up. When a starter is matured, it regularly doubles or triples in volume after a feeding. With consistent feedings, it gets a kind of metabolism where you can rely on it reaching peak activity at a specific time during the day. It can help to use the starter as it approaches its peak activity, especially if we’re working with a cold kitchen.</p>
<p>Watch out for the infamous starter false start: Around day 3 or 4, the starter will <em>spike</em> with massive growth and beginners will rejoice about how ready their starter is but it is for for nought: This happens because an adjacent bacteria took hold of the starter and consumed all of the nutrients until it burned itself out. With consistent feedings, the desired yeast sourced from our flour grains will ultimately prevail and develop a healthy cadence.</p>
<h3 id="time"><strong>Time</strong></h3>
<p>Aside from changing the temperature, and the quantity and activity of our starter, we’ll need to adjust the timing of our steps. If the kitchen is cold, ferment and proof longer. If the yeast is not too active, extend our timeline even further. It can help a lot to learn what the quality of the dough should be at each step, to know whether to proceed to the next step or wait longer. There are various tests we can do, such as the floating test to see if the starter is ready to be used; the windowpane test to see if we have sufficient gluten development; the poke test to see if the dough is sufficiently proofed for baking. We’ll go more into it in the <em>Steps</em> section.Our dough can be underproofed or overproofed, and we might not even realize it until we pull it out of the oven, wait the prerequisite hour, cut into it, share our joy on instagram and then receive a DM from a professional baker that just says “it’s underproofed.”</p>
<p>An underproofed bread won’t have enough structure to fully rise during the bake, all of the gasses from the yeast will accumulate into few large bubbles, parts of it will be very dense and maybe even raw tasting. It’s much easier to underproof as a beginner, especially with an immature starter and a cold kitchen.</p>
<p>An overproofed bread won’t have an ideal crumb, such as very small holes, or it could collapse under its own rise because the gluten relaxed too much. If we discover the dough is overproofed before baking, we can fold it a few more times to reactivate the gluten and give it another short proof before baking. I’ve found it hard to find consensus on what qualities an overproofed loaf has, so it’s safer to err on the side of overproofing than underproofing if we must.</p>
<p>For other kinds of baking, when we just mix some ingredients and toss them into a temperature-controlled oven, it’s appropriate to follow the recipe to the letter. For sourdough bread baking, we need to be ready to adjust the recipe to fit our environment and our needs.</p>
<h2 id="steps">Steps</h2>
<p>Many recipes have a lot of steps in common. Some of them are necessary, others can be substituted or adjusted, others are purely ritual. It’s useful to know which is which, and what we can do with them.</p>
<p>Maybe we started our bread too late in the day and can’t stay up until 2AM to do 6 additional folds, or maybe we don’t have time to bake in the morning and would prefer to avoid refrigerating overnight.</p>
<ol>
<li>
<p><strong>Levain:</strong> This is a process for taking some of your existing starter and making a separate sister starter that we can use for making our loaf while keeping our original starter.</p>
<p>🤔 If we have a large enough discard from our original starter, we can use that instead.</p>
</li>
<li>
<p><strong>Autolyse:</strong> Typically done while the levain is prepared, it’s for mixing the bulk of the recipe’s flour and water separate from the starter and letting it rest until the levain is ready to be mixed.</p>
<p>🤔 If we’re skipping the levain step, skipping the autolyse step is not catastrophic for beginners. We can mix our discard all together, or I prefer to mix most of the water with the discard first and then add the bulk of the flour.</p>
</li>
<li>
<p><strong>Mixing Techniques:</strong> As a beginner, do whatever it takes to get everything combined into a shaggy doughy mess. Minimize how much flour spills over the counter and makes a mess, and try to scrape what we can from our hands, but it’s not a big deal. We can slap, we can fold, we can slam, we can even yell profanities at it while we pinch it a bunch.</p>
</li>
<li>
<p><strong>Rest and Add Salt:</strong> Many recipes are <em>very serious</em> about leaving the salt and a bit of water until after mixing the bulk of the dough and water. The theory is that salt leeches moisture from the flour so it won’t be absorbed as evenly if we do it all at once. Once we mix in the salt, the feel of the dough instantly changes into something much less shaggy and less sticky, it’s delightful.</p>
<p>🤔 <a href="https://www.youtube.com/watch?v=Z0o-tkaDqps">Foodgeek did an A/B test on this</a>, and it doesn’t seem to matter if we mix in the salt with the Autolyse step. Seems it’s more ritual than anything, so no need to panic if we mixed in salt prematurely. It is convenient to save a bit of our water reserve for mixing the salt, though.</p>
</li>
<li>
<p><strong>Stretch and Fold:</strong> During the “bulk fermentation” step, we’re usually instructed to perform a number of folds to help develop the gluten in the dough so that the webby network of the bread will have enough structure to support itself around all of the lovely bubbles produced by the yeast as it rises and bakes. Different recipes have different techniques here, with different timing. Some recipes suggest using oiled hands for working with dough at this point, but water-wet hands are even better and less messy and doesn’t introduce oil to the dough which can affect the recipe composition.</p>
<p>🤔 <a href="https://www.youtube.com/watch?v=Lz9CO1PJ0sM">Another Foodgeek contribution on the topic</a>, it certainly seems worth doing at least a couple of good dough perturbations over a few hours. With a sufficiently mature yeast, even doing nothing produces decent sourdough but not quite as good and we’re taking a gamble on the activity of our yeast to compensate. If we’re busy, do at least two nice gentle lift and coil sets spaced out by 30–60 min, and let it rest until it rises 50–100%.</p>
<p>🤔 Do <a href="https://www.youtube.com/results?search_query=sourdough+windowpane+test">the window pane test</a> if we’re unsure if it needs to rest more. Colder environments or less mature yeast could require more time than prescribed here.</p>
</li>
<li>
<p><strong>Fold, Roll, and Stitch:</strong> By shaping our dough at this point, we create a tense form that will present the rough shape of the final loaf. With a good tight roll and stitch, the loaf will have a better chance to rise vertically rather than sprawl into a blob.</p>
<p>🤔 I’ve unintentionally totally for science skipped this step once and ended up with a very flat loaf that sprawled the circumference of the dutch oven. As far as technique goes, this is one of the harder steps but it pays off to get better at it.</p>
</li>
<li>
<p><strong>Banneton, Seam Side Up:</strong> The shape of the banneton helps reinforce the final form of the loaf as we give it additional time to rise. If we don’t have a banneton, we can use any form that is not too much bigger than the desired loaf size. I use a colander lined with a linen towel.</p>
<p>🤔 This is the companion step to the Fold, Roll, and Stitch. It’s similarly important, but the seam side up is not a huge deal if we messed up. The loaf won’t have that perfect smooth top to work with on the surface, but it will be just as good inside. Remember to dust the lining with flour so it won’t stick, or some corn meal if we’re feeling fancy.</p>
</li>
<li>
<p><strong>Overnight in the Fridge:</strong> Proofing in the fridge is a recurring step from all bakers who love to bake first thing in the morning. What about the hungry night owls? What about all the sourdough bakers who have been making this lovely bread for far long than refrigerators have existed in our society?</p>
<p>🤔 We can replace the fridge proofing with a 3–5 hour proof at room temperature, on the longer end if the temperature is low or the starter is less active. Do the poke test if we’re unsure if it’s ready: The recess in the dough from the poke shouldn’t spring back immediately when it’s ready. Cover in plastic or wrap in a bag to prevent the dough from drying out while it’s proofing, especially if we’re refrigerating overnight.</p>
</li>
<li>
<p><strong>Pre-heat and Bake From Cold:</strong> Pre-heat the oven to maximum heat, typically 500F, then quickly take out the dough from the fridge, flip onto parchment (since we did seam-side up), dust with some flour, spread the flour, score with a lame (a fancy razor holder), and commit it to the flames.</p>
<p>🤔 If we don’t have a dutch oven, or a covered cast iron pot, we can use a baking steel or even a baking sheet. We’ll need another pan placed at a lower rack full of boiling water, to add the moisture to the oven necessary for a nice crispy crust. The lidded dutch oven captures and retains the moisture from the dough, so it’s a bit simpler.</p>
<p>🤔 What if the dough is looking a little sad after an overnight in the fridge? This can happen if the yeast isn’t as active as it could be. Take it out from the fridge and give it one more room-temperature proof for 2–4 hours, bonus points if we can proof it somewhere a bit warmer (80–85F), like a seedling warming mat.</p>
<p>🤔 No lame? No problem. If we use a safety razor, carefully use a clean razor by hand. Worst case, just grab a nice sharp knife and use that to score. End to end, leaning 45 degrees, half-inch deep cut. Not a huge deal if the scoring step is botched or skipped, the bread will still form just fine but it won’t expand along a specific gorgeous seam of your making.</p>
</li>
<li>
<p><strong>Wait For The Hardest Hour Of Your Life:</strong> When we pull the loaf out of the oven, it’s not quite done baking. A rookie mistake is to huddle around the glorious steaming orb-treasure and tear into it like a pack of post-apocalyptic savages. A true intellectual will place an ear near the crust as it cools and hear the crackling transformation within.</p>
<p>🤔 If we don’t wait, it will still be edible but: The inside will be extra stretchy or gummy, it might have an “undercooked” vibe to it, some of the final notes of flavour won’t quite develop. Seriously, just wait at least an hour, or until it fully reaches room temperature. If it’s steamy when we cut into it, we didn’t wait long enough.</p>
</li>
</ol>
<h2 id="you-can-sourdo-ugh-it">You Can Sourdo-ugh It</h2>
<p>Let’s be real, we’re all in this for the sourdough puns.</p>
<p>Here’s a list of ideas we can try if it’s just not working out:</p>
<ul>
<li>Suspect the starter is just not ready? Use extra starter, like 30%. If you desperately want vaguely sourdough-y bread and can’t wait, sprinkle a bit of instant yeast when you mix in the starter (half teaspoon should do).</li>
<li>Other kinds of breads to make while the starter is maturing? <a href="https://cooking.nytimes.com/recipes/11376-no-knead-bread">No-Knead recipes</a> are great, or Almost No Knead. I like ones with a lager beer. I’m a fan of sourdough-hybrid chimera breads. To improvise a recipe, substitute some of the hydration with sourdough starter (remember it’s 100% hydration dough, or 1:1), and substitute some of the water with beer.</li>
<li>Keep making that discard when you feed, sometimes I’ll keep the feeding ratio but skip a discard to have an extra-big discard the next day. Lots of things we can use that discard for, or even just toss some globs on a frying pan and make crumpets.</li>
<li>Starter not starting? Try a different flour. The bulk of the yeast in the starter comes from the grains that the flour is made of, and sometimes cheaper flours aren’t the best source of a good healthy yeast — especially avoid bleached flour. Buy the fanciest bread flour available, just to be sure. Try mixing in 5–20% whole wheat or rye in there for extra protein. Some restaurants or bakeries will give away (or sell for super cheap) their starter, if we’re desperate to bootstrap from a known quantity.</li>
</ul>
<p>Even if mistakes are made along the way, there’s a good chance we’ll end up with a decent tasting bread product in the end. We must eat our shame as we will our inevitable victory.</p>
<h2 id="additional-resources">Additional Resources</h2>
<ul>
<li><a href="https://www.theperfectloaf.com/simple-weekday-sourdough-bread/">The Perfect Loaf: Simple Weekday Sourdough Bread</a>: Good comprehensive recipe with a handy timeline visualization, good for beginners.</li>
<li>Video: <a href="https://www.youtube.com/watch?v=jJpIzr2sCDE">The Ultimate Homemade Sourdough Bread</a>: Charismatic long-haired fella with a large Youtube audience making sourdough, good instruction for beginners.</li>
<li>Video: <a href="https://www.youtube.com/watch?v=l0Ob6v8GpM0">Tartine Country Loaf by knormie</a>: Nice simple quick video showing the process and techniques.</li>
<li>Video: <a href="https://www.youtube.com/watch?v=oidnwPIeqsI">Brad and Claire Make Sourdough</a>: Hilarious but some advanced techniques without much context for beginners. Honestly this is what got me started, which did not yield the best first result. Feel free to skip until later.</li>
<li><a href="https://foodgeek.dk/en/">Foodgeek Blog</a> and <a href="https://www.youtube.com/channel/UC7eLtGAzNECUqurqMdiNYJg">Foodgeek Videos</a>: Big fan of this, much of what I learned for this post was based on Sune’s work of methodically testing out variations of every variable in a sourdough recipe.</li>
<li><a href="https://www.reddit.com/r/Sourdough/">/r/Sourdough</a>: Obligatory topical subreddit. Can’t go wrong. More resources in the sidebar.</li>
<li><a href="https://breadcalc.com/">Bread Hydration and Conversion Calculator</a>: Anytime I start a loaf, I plug in my intended components here, make some tweaks to reach the desired hydration, and dive in.</li>
</ul>
<p>🍞</p>

      ]]></content:encoded></item><item><title>Open sourcing urllib3’s finances</title><link>https://shazow.net/posts/open-sourcing-urllib3s-finances/</link><pubDate>Mon, 06 May 2019 21:13:09 +0000</pubDate><guid>https://shazow.net/posts/open-sourcing-urllib3s-finances/</guid><description>&lt;blockquote class="twitter-tweet">&lt;p lang="en" dir="ltr">Proud to be part of the team who built this chonk of vital internet infrastructure. ❤️&lt;br>&lt;br>Big sincere thank you to our maintainers: &lt;a href="https://twitter.com/theavalkyrie?ref_src=twsrc%5Etfw">@theavalkyrie&lt;/a> @sethmlarson &lt;a href="https://twitter.com/haikuginger?ref_src=twsrc%5Etfw">@haikuginger&lt;/a> &lt;a href="https://twitter.com/Lukasaoz?ref_src=twsrc%5Etfw">@Lukasaoz&lt;/a> &lt;a href="https://twitter.com/sigmavirus24?ref_src=twsrc%5Etfw">@sigmavirus24&lt;/a>, and to all the other contributors for this very important work. &lt;a href="https://t.co/ea3iFClKD5">https://t.co/ea3iFClKD5&lt;/a>&lt;/p>&amp;mdash; Andrey 🦃 Petrov (@shazow) &lt;a href="https://twitter.com/shazow/status/1074729655394074624?ref_src=twsrc%5Etfw">December 17, 2018&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>


&lt;p>Since &lt;a href="https://github.com/urllib3/urllib3">urllib3&lt;/a> was created in 2008, we have gained several amazing maintainers and hundreds of contributors. Any time you do anything that touches HTTP in Python, you’re probably using urllib3 behind the scenes. It is the 2nd most downloaded third-party Python package, after pip.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <blockquote class="twitter-tweet"><p lang="en" dir="ltr">Proud to be part of the team who built this chonk of vital internet infrastructure. ❤️<br><br>Big sincere thank you to our maintainers: <a href="https://twitter.com/theavalkyrie?ref_src=twsrc%5Etfw">@theavalkyrie</a> @sethmlarson <a href="https://twitter.com/haikuginger?ref_src=twsrc%5Etfw">@haikuginger</a> <a href="https://twitter.com/Lukasaoz?ref_src=twsrc%5Etfw">@Lukasaoz</a> <a href="https://twitter.com/sigmavirus24?ref_src=twsrc%5Etfw">@sigmavirus24</a>, and to all the other contributors for this very important work. <a href="https://t.co/ea3iFClKD5">https://t.co/ea3iFClKD5</a></p>&mdash; Andrey 🦃 Petrov (@shazow) <a href="https://twitter.com/shazow/status/1074729655394074624?ref_src=twsrc%5Etfw">December 17, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<p>Since <a href="https://github.com/urllib3/urllib3">urllib3</a> was created in 2008, we have gained several amazing maintainers and hundreds of contributors. Any time you do anything that touches HTTP in Python, you’re probably using urllib3 behind the scenes. It is the 2nd most downloaded third-party Python package, after pip.</p>
<p>Tens of thousands of engineering hours were <em>donated</em> by people working on their own time, unpaid. The whole world benefited from these important donations.</p>
<p>Generous companies have helped the urllib3 contributors and maintainers financially over the years. In the spirit of transparency, I’m going to take a moment to talk about some of our recent funding and how that money was spent.</p>
<h3 id="supportive-employers">Supportive Employers</h3>
<p>Above all, I want to applaud employers who are actively encouraging their team to contribute back to open source as part of their job. <a href="https://github.com/urllib3/urllib3#sponsorship">You’re already part of our README</a>, but once more for the active supporters:</p>
<ul>
<li>Thank you <a href="https://cloud.google.com/">Google Cloud Patform</a> for supporting <a href="https://github.com/theacodes">Thea</a>, our lead maintainer.</li>
<li>Thank you <a href="https://www.abbott.com/">Abbott</a> for supporting <a href="https://github.com/sethmlarson">Seth</a>, who has been doing the bulk of the work for a long time now.</li>
</ul>
<p>And thank you to all of the employers who have contributed your company’s resources to our project in the past: <a href="https://www.akamai.com/">Akamai</a>, <a href="https://www.hpe.com">Hewlett Packard Enterprise</a>, and others. If your work on urllib3 is actively being funded by your employer, please send a pull request to be added to the README.</p>
<h3 id="grants-and-sponsorships">Grants and Sponsorships</h3>
<p>We have received a variety of support in various shapes and sizes over the years. Most recently:</p>
<ul>
<li>Thank you <a href="https://www.govcert.lu/">Computer Emergency Response Team of the Government of the Grand Duchy of Luxembourg</a> for funding 8 weeks of Seth’s time towards improving urllib3’s security and robustness.</li>
<li>Thank you <a href="https://gitcoin.co/grants/65/urllib3">Gitcoin</a> for allocating over $6,000.00 in funds over 3 months that will go towards Seth’s time on urllib3.</li>
</ul>
<p>Additionally, <a href="https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&amp;utm_medium=referral&amp;utm_campaign=blog">urllib3 joined Tidelift Subscription</a> to provide better support guarantees for their paying customers. Our current revenue from the subscription is $417.00 per month ($10,000 over 2 years). This funding is going to Seth for handling support requests and releases. Previously, I was taking $208.50 per month for work as backup maintainer. Thank you <a href="https://tidelift.com">Tidelift</a>!</p>
<h3 id="does-your-company-benefit-from-python">Does your company benefit from Python?</h3>
<p>With over 50 million installs per month, urllib3 is a core part of the world’s infrastructure. There is a never ending stream of improvements and fixes that need to be done, especially as HTTP continues to evolve as a technology.</p>
<p>If you can convince your management to allocate a budget to support the improvement of urllib3, please <a href="https://keybase.io/shazow">get in touch with me</a> or <a href="https://keybase.io/theacodes">Thea</a>. We will find the right urllib3 project and contributor to make good use of the funding, and the whole world will benefit from it.</p>
<p>Thank you.</p>

      ]]></content:encoded></item><item><title>My computer is my home</title><link>https://shazow.net/posts/my-computer-is-my-home/</link><pubDate>Thu, 08 Mar 2018 16:34:36 +0000</pubDate><guid>https://shazow.net/posts/my-computer-is-my-home/</guid><description>&lt;p>&lt;img src="https://shazow.net/posts/my-computer-is-my-home/images/1.png#layoutTextWidth" alt="image">&lt;/p>
&lt;p>I spent fifty hours setting up my new computer, and I feel a mixture of shame and pride about it.&lt;/p>
&lt;p>We spend a lot of our lives in our computers. Working, socializing, relaxing. On the work laptop, at the home desktop, on the phone in bed.&lt;/p>
&lt;p>We live in our computers, and we do little things to make ourselves feel more at home in them — a little more cozy. We change the wallpaper, install some new apps, buy a pretty case and some stickers for the back. We find ways to make our devices different from other peoples’. We personalize what we can. What we’re permitted.In 2008, I bought my first iPhone. Shortly after, I bought my first Macbook Pro. Coming from Linux, the Apple ecosystem felt clean and fresh. It felt like checking into a hotel. Everything I will need was laid out on the counter, and anything else could be arranged by the concierge (perhaps for a fee). I made myself at home.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p><img src="/posts/my-computer-is-my-home/images/1.png#layoutTextWidth" alt="image"></p>
<p>I spent fifty hours setting up my new computer, and I feel a mixture of shame and pride about it.</p>
<p>We spend a lot of our lives in our computers. Working, socializing, relaxing. On the work laptop, at the home desktop, on the phone in bed.</p>
<p>We live in our computers, and we do little things to make ourselves feel more at home in them — a little more cozy. We change the wallpaper, install some new apps, buy a pretty case and some stickers for the back. We find ways to make our devices different from other peoples’. We personalize what we can. What we’re permitted.In 2008, I bought my first iPhone. Shortly after, I bought my first Macbook Pro. Coming from Linux, the Apple ecosystem felt clean and fresh. It felt like checking into a hotel. Everything I will need was laid out on the counter, and anything else could be arranged by the concierge (perhaps for a fee). I made myself at home.</p>
<p>Over the years, I started to feel disassociated from my devices. They were mine, but they didn’t feel mine. The immersion of ownership would frequently break as a corporate policy kicked my favorite app out of their store, or a feature I wanted was forbidden by the exposed API, or the pressure to buy newer hardware grew each year with forced software upgrades.</p>
<p>Imagine living in a hotel. You check in, put your backpack down on the luggage rack, and kick off your shoes. It’s a little different from your last hotel, but also the same. A television that you avoid, not because there is work to do but because the remote looks kind of gross. A lamp that is using up the only power outlet next to the bed, which you need for charging your phone overnight. There is a desk, but the padded office chair doesn’t lift enough to use it comfortably.</p>
<p>You’ll be in the hotel for a while, so you can make yourself comfortable and add a bit of yourself to it. You request a <a href="https://www.cnbc.com/id/47890272">fish bowl from the concierge</a>, and you name the fish <em>Chee</em> (short for <em>Cheeto</em>). You bring some chocolates from the Quik Mart a few streets away. You play some music on the complementary bluetooth speaker.</p>
<p>Ultimately, the hotel is an organism that operates outside of you. The unfinished chocolates disappear after a cleaning. The speaker keeps disconnecting sporadically. <a href="https://www.peta.org/blog/making-waves-hotel-chain-ends-fish-rental-program-with-petas-help/">They take Chee away</a>.</p>
<p>I switched to Android a few years back (after Lollipop came out, the first <em>good</em> Android). This week, I am giving up my fourth Macbook Pro — my final Apple device. Apple makes beautiful devices that were unrivaled for a long time, but convenience and uniformity comes at a price. I have come to crave <em>fragmentation</em>.The shame is obvious: I didn’t spend those fifty hours doing things like building better insulin control software for diabetic people or campaigning for affordable housing.</p>
<p>I spent those hours building my perfect computer home. Like a fresh cabin in the wilderness, there were some false starts. Some kernel modules were missing, or the full-disk encryption was misconfigured. At one point, about twenty hours in, I knocked it all down and started from the foundation again.</p>
<p>I learned very much — about binary firmware embedding in the kernel and LUKS disk encryption and systemd quirks.</p>
<p>More than anything, I made it feel <em>mine.</em> I will keep tweaking everything throughout the lifetime of <a href="https://www3.lenovo.com/us/en/laptops/thinkpad/thinkpad-x/ThinkPad-X1-Carbon-6th-Gen/p/22TP2TXX16G">this laptop</a> and I’m sure much of the configuration will survive to the next. Lay out some area rugs, new cutlery, softer lighting, art for the walls. Hopefully not fifty hours in one week, but a few hours here and there. With each improvement, I will feel more at home, and there is no limit to what I can do.</p>
<p>I am proud of my new computer home.</p>

      ]]></content:encoded></item><item><title>How to hand over an Open Source project to a new maintainer</title><link>https://shazow.net/posts/how-to-hand-over-an-open-source-project-to-a-new-maintainer/</link><pubDate>Fri, 09 Feb 2018 13:16:01 +0000</pubDate><guid>https://shazow.net/posts/how-to-hand-over-an-open-source-project-to-a-new-maintainer/</guid><description>&lt;p>After maintaining a project for many years, maybe you’re no longer using it yourself? Or maybe you’re burned out from the grind? Or you can’t afford to allocate time to it? Maybe it’s time to pass it on to somebody else.&lt;/p>
&lt;p>I feel like there is more to learn from failure stories than success stories, so let’s start with those.&lt;/p>
&lt;h2 id="what-not-to-do">What not to do&lt;/h2>
&lt;p>I’ve handed over several projects before, and some didn’t go as well as I’d hoped. Here are some mistakes I’ve made:&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>After maintaining a project for many years, maybe you’re no longer using it yourself? Or maybe you’re burned out from the grind? Or you can’t afford to allocate time to it? Maybe it’s time to pass it on to somebody else.</p>
<p>I feel like there is more to learn from failure stories than success stories, so let’s start with those.</p>
<h2 id="what-not-to-do">What not to do</h2>
<p>I’ve handed over several projects before, and some didn’t go as well as I’d hoped. Here are some mistakes I’ve made:</p>
<h3 id="1-dont-transfer-the-project-to-the-new-maintainers-namespace">1. Don’t transfer the project to the new maintainer’s namespace.</h3>
<blockquote>
<p>Once upon a time, I had given up developing <em>s3funnel</em>, a multi-threaded S3 client. I just wasn’t using S3 anymore, and I had no desire or need to support new features. I announced that I’m looking for new maintainers, and a contributor volunteered. Great! I transferred the project to his ownership, Github kindly permanently redirected, and the project moved along without me.&gt; Couple years later, the new maintainer also gave up on <em>s3funnel</em> and largely disappeared. I don’t blame him. It wasn’t even his project—it wasn’t his <em>baby</em>. A bunch of pull requests piled up, unanswered. I wanted to put in a few hours to merge them, but I couldn’t. This was my mistake.</p>
</blockquote>
<p><strong>Don’t expect the new maintainer to stick around longer than you did.</strong> If the maintainer leaves without finding a replacement, you need to be able to take back the project. You can add new maintainers as collaborators on your repository or move the repository to an organization namespace which can have teams added and removed over time.</p>
<h3 id="2-dont-micromanage-the-new-maintainer">2. Don’t micromanage the new maintainer.</h3>
<p>I don’t have an anecdote here, but really: The entire point of handing over a project is to release yourself from its burden. Let go, for your own sake.</p>
<p>Similarly, as a new maintainer of someone else’s project: Maybe they’re doing it because they want the responsibility and experience of managing other people—being the boss. They’re certainly not doing it to gain yet another boss. Let go, for your maintainer’s sake.</p>
<p>This doesn’t mean that you need to disassociate yourself entirely, but it does mean you need to defer to the new maintainer’s authority. You are just another contributor now, not the lead. <strong>Respect the new leader and empower them.</strong> I promise that a maintainer who feels respected and empowered will do better and stick around longer.</p>
<h3 id="3-dont-expect-more-from-the-new-maintainer-than-theyre-giving">3. Don’t expect more from the new maintainer than they’re giving.</h3>
<p>Unless you’re paying and employing the new maintainer to take over as part of their job description, they don’t owe you anything.</p>
<p>The new maintainer has no obligation to stick around forever. It’s not ideal, but it’s important to respect that the new maintainer is well within their rights to disappear.</p>
<p>Check in once in a while and make sure things are still going okay and their lifestyle permits them to continue as the maintainer. Maintaining open source is hard work, support them if they need to give back the torch/albatross.</p>
<h2 id="how-to-do-it-right">How to do it right</h2>
<p>So far, <a href="https://github.com/shazow/urllib3">urllib3</a> has enjoyed <a href="https://github.com/shazow/urllib3/issues?q=label%3AAnnouncement">two successful maintainer transitions</a>. Here’s what worked well:</p>
<h3 id="finding-a-new-lead-maintainer">Finding a new lead maintainer</h3>
<p>It can be tempting to put out a public call for new maintainers, but that’s best left as a last resort. The goal is to find someone who is already familiar with the codebase and has shown some commitment to working on it over time. We want to avoid choosing a maintainer who didn’t know what they were getting into and have them abandon the project a week later, forcing us to go through the whole process again.</p>
<p>I’ve found good results in reaching out to specific people who would be good candidates. In both cases for <em>urllib3</em>, the new lead maintainers were long time contributors to the project. Even better if they’re already co-maintainers. <strong>Even double-better if their employer allows them to maintain the project as part of their job.</strong> It doesn’t hurt to ask, and it helps <em>a lot</em>.</p>
<h3 id="foster-a-team-of-co-maintainers">Foster a team of co-maintainers</h3>
<p><a href="https://github.com/shazow/urllib3#maintainers">Additional maintainers</a> are there to help the lead maintainer. They’re the go-to for handling trivial issues, doing code reviews, or taking random tasks when time permits. When it’s time to choose a new lead maintainer, the co-maintainers are a great place to start from. The co-maintainers are there to help with the transition, too. It’s less scary when you’re not alone.</p>
<h3 id="make-an-explicit-announcement-for-the-transition">Make an explicit announcement for the transition</h3>
<blockquote>
<p><a href="https://twitter.com/shazow/status/939957174155046913"></a></p>
</blockquote>
<p>Putting everything in <a href="https://github.com/shazow/urllib3/issues?q=label%3AAnnouncement">a project issue labelled <em>Announcement</em></a> and linking to it everywhere for discussion and transparency has been great.</p>
<h3 id="have-a-checklist-for-what-that-needs-to-be-done">Have a checklist for what that needs to be done</h3>
<p>I like keeping this checklist public, alongside the announcement, because it helps raise awareness that this person is now getting access to all these things for the project.</p>
<ul>
<li>Announcement post</li>
<li>Update README and documentation</li>
<li>Repository collaborator/admin access (e.g. Github)</li>
<li>Release publishing access (e.g. PyPi)</li>
<li>Docs management access (e.g. ReadTheDocs)</li>
<li>Continuous Integration admin access (e.g. TravisCI)</li>
<li>Signing key access, or publish the new signing key (e.g. for GPG signed releases)</li>
<li>Confirm that the release process is documented and achievable by the new maintainer</li>
<li>Subscribe the new maintainer to the relevant StackOverflow tags</li>
</ul>
<p>Switching to a new maintainer hands over a lot of trust to this person. That’s the nature of the gig. More transparency about the powers that this role comes with helps the users plan their security models accordingly.</p>
<h2 id="but-for-now">But for now…</h2>
<p>I feel like we all hope that our work will outlive us, in one way or another. Handing over the reins of a project is a natural part of a successful project, and something that will become more common as the open source community continues to mature.</p>
<p>When the time comes, let’s do it right.</p>

      ]]></content:encoded></item><item><title>Quitting Twitter… ish.</title><link>https://shazow.net/posts/quitting-twitter-ish/</link><pubDate>Fri, 05 May 2017 01:10:26 +0000</pubDate><guid>https://shazow.net/posts/quitting-twitter-ish/</guid><description>&lt;h4 id="it-has-been-over-two-months-and-i-feel-better">It has been over two months and I feel better.&lt;/h4>
&lt;blockquote class="twitter-tweet">&lt;p lang="en" dir="ltr">&lt;a href="https://twitter.com/hyfen?ref_src=twsrc%5Etfw">@hyfen&lt;/a> pong&lt;/p>&amp;mdash; Andrey 🦃 Petrov (@shazow) &lt;a href="https://twitter.com/shazow/status/34501532?ref_src=twsrc%5Etfw">April 20, 2007&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>


&lt;p>My first tweet.&lt;/p>
&lt;p>I haven’t tweeted in over two months.&lt;/p>
&lt;p>This is very unusual for me. I’ve been an avid tweeter since 2007. Writing tweets has become a part of my identity to the point where most my bios mention that I’m a tweet ghostwriter—composing and editing tweets for friends is one of my favourite things to do.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <h4 id="it-has-been-over-two-months-and-i-feel-better">It has been over two months and I feel better.</h4>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr"><a href="https://twitter.com/hyfen?ref_src=twsrc%5Etfw">@hyfen</a> pong</p>&mdash; Andrey 🦃 Petrov (@shazow) <a href="https://twitter.com/shazow/status/34501532?ref_src=twsrc%5Etfw">April 20, 2007</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<p>My first tweet.</p>
<p>I haven’t tweeted in over two months.</p>
<p>This is very unusual for me. I’ve been an avid tweeter since 2007. Writing tweets has become a part of my identity to the point where most my bios mention that I’m a tweet ghostwriter—composing and editing tweets for friends is one of my favourite things to do.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Sometimes I worry the NSA might be spying on me through my desktop&#39;s webcam. Then I remember it&#39;s Linux and even I can&#39;t get it to work.</p>&mdash; Andrey 🦃 Petrov (@shazow) <a href="https://twitter.com/shazow/status/437668894916890624?ref_src=twsrc%5Etfw">February 23, 2014</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<p>My most popular tweet.</p>
<p>I stopped reading and posting to Twitter for my mental health, but it’s not just about Twitter.</p>
<h3 id="mental-health">Mental health</h3>
<p>Modern life is a minefield of addiction. I’m not talking about substance addiction, but behavioural addiction. News with clickbait titles, discussions filled with polarizing trolls, mobile games with clever viral loops, social media with a never-ending stream of dopamine hits.</p>
<p>Do you ever spend an hour just <em>reading news</em>? New tabs sprouting faster than we can close them. Just when we get close, we’re back to where we started—checking Twitter again, or Reddit, or Hacker News, or Slack. A few moments passed, so there is plenty of fodder for some fresh tabs to work through.</p>
<p>Do you ever feel justified being inside the feedback cycle? It feels like learning, but do we ever learn anything? It feels like <em>engaging</em>, or is it fluff? Are we even paying attention? Or are we coasting on autopilot with a machete through a never-ending forest of tightly-packed tidbits because that’s what we’ve trained ourselves?</p>
<h3 id="doing-something-about-it">Doing something about it</h3>
<p>I started meditating daily exactly one year ago. I’ve been using Headspace, but my year subscription is about to run out and I’ll probably move onto something else.</p>
<p>Eventually, I noticed something: Meditation calmed my mind. Duh. But I noticed something else: Reading social media and news would make my mind more turbulent. After catching up on my Twitter feed, I would be become distractible and unmotivated. I noticed that my willpower would diminish and I would get depressed, falling further into the cycle of feeding on news because it’s the only thing that felt easy enough to do.</p>
<p>For a while, I used meditation as a counterbalance to binging on the hail of micro dopamine hits. It helped, but it was not enough.</p>
<h3 id="removing-triggers">Removing triggers</h3>
<p>For the past year, I looked for things in my life that trigger me into negative behaviour. Things that make me less than the kind of person I aspire to be. Removing them has been difficult, and I’ve found that the best way for me has been to pick one thing at a time and quit it cold turkey. Once my lifestyle adapted to its absence, I could move on to the next thing.</p>
<p>Most recently, I’ve been focusing on quitting reading discussions which draw me into petty arguments and quitting news sources that are infinitely abundant. This means quitting some Slacks, not reading the Reddit front page, and not reading Twitter.</p>
<p>It has been two months. I’ve been more productive and my mood has been more stable, but I still feel the temptation to fall back into that cycle. I need to be careful.</p>
<h3 id="moving-forward">Moving forward</h3>
<p>Since I eliminated Twitter, I started writing more long-form content again. I want to encourage myself to do more of that and I want to share my posts with my followers, so I’m going to try a new set of rules for myself:</p>
<ul>
<li>I’m not going to read my Twitter feed.</li>
<li>I’m going to avoid replying on Twitter (or on social media in general) and instead try to have more private conversations instead. Please DM me when possible.</li>
<li><strong>I am only going to post on my Twitter to share long-form content.</strong></li>
</ul>
<p>For people I’m following, I’m trying to subscribe to your blogs and newsletters whenever possible.</p>
<p>If you’d like to catch up, share something cool with me, or just miss my tweets: <a href="https://keybase.io/shazow">Send me a message and we can chat!</a></p>

      ]]></content:encoded></item><item><title>Code boilerplate: Is it always bad?</title><link>https://shazow.net/posts/code-boilerplate-is-it-always-bad/</link><pubDate>Mon, 01 May 2017 16:47:49 +0000</pubDate><guid>https://shazow.net/posts/code-boilerplate-is-it-always-bad/</guid><description>&lt;blockquote>
&lt;p>A case study between Python and Go.&lt;/p>
&lt;/blockquote>
&lt;p>In Go, a common complaint from newly-minted gophers who come from another language is the error handling pattern:&lt;/p>
&lt;pre tabindex="0">&lt;code>result, err := DoSomething()
if err != nil {
 return nil, err
}
&lt;/code>&lt;/pre>&lt;pre tabindex="0">&lt;code>another, err := SomethingElse(result)
if err != nil {
 return nil, err
}
&lt;/code>&lt;/pre>&lt;pre tabindex="0">&lt;code>// We can be a bit more terse if we don&amp;#39;t need to save any
// variables outside of the if-scope:
if err := MoreWork(another); err != nil {
 return nil, err
}
...
&lt;/code>&lt;/pre>&lt;p>Any time we write similar-looking code over and over, that’s considered &lt;em>boilerplate&lt;/em>.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <blockquote>
<p>A case study between Python and Go.</p>
</blockquote>
<p>In Go, a common complaint from newly-minted gophers who come from another language is the error handling pattern:</p>
<pre tabindex="0"><code>result, err := DoSomething()
if err != nil {
    return nil, err
}
</code></pre><pre tabindex="0"><code>another, err := SomethingElse(result)
if err != nil {
    return nil, err
}
</code></pre><pre tabindex="0"><code>// We can be a bit more terse if we don&#39;t need to save any
// variables outside of the if-scope:
if err := MoreWork(another); err != nil {
    return nil, err
}
...
</code></pre><p>Any time we write similar-looking code over and over, that’s considered <em>boilerplate</em>.</p>
<p>A key principle of programming that we all learn is <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">Don’t Repeat Yourself</a> (DRY). In a way, that’s the entire point of programming: To automate a task that we keep repeating manually. Who wants to keep artisanally sorting text files each morning when we could have a program do it for us faster and more reliably? Similarly, why should we keep writing some code pattern over and over when we should be able to abstract it away into some terser syntax?</p>
<h2 id="proportional-work">Proportional work</h2>
<p>After writing Python code for over a decade and becoming quite proficient at it, I’d take any opportunity to write terse code. It feels good to be clever and <a href="https://www.mayerdan.com/ruby/2012/11/11/bugs-per-line-of-code-ratio">more code means more bugs</a>, right?</p>
<p>One thing I didn’t notice until I switched to writing Go as my primary language: The <em>proportionality of work</em> done in Python code is hugely variable.</p>
<p><strong>What do I mean by proportionality?</strong> Consider these two lines:</p>
<pre tabindex="0"><code>a = some_variable + 42
b = sum((j if j % 2 else 0) for (j, k) in results if k)
</code></pre><p>The second line does <em>a lot</em> more work than the first line. The first line is a simple integer addition, while the second line has a loop and a variable split and two conditional branches and an accumulator on a generator.</p>
<p>In Python (and many other languages), it’s very easy to unintentionally hide <em>tons</em> of work in a single line of built-in syntax. Some lines do almost no work, other lines do a moderate amount of work, other lines do what I’d describe is tens of lines worth of work—a lot of work. It varies a lot, even in beginner code.</p>
<p>In Go, I’ve found that this variability is much more diminished. A loop is always a loop, an error check is always an error check (rather than implicit exception propagation), a logic branch is always a branch.</p>
<p>This is the most reasonably-terse way I could write the above in Go:</p>
<pre tabindex="0"><code>b := 0
for _, r := range results {
    j, k := r[0], r[1]
    if !k || j % 2 == 0 {
        continue
    }
    b += j
}
</code></pre><p>Is this good? I believe it is. I believe this code is more proportional to the complexity of the work that it’s doing. It’s also possible to write Python in a similar level of proportionality, why don’t I?</p>
<p>When reading proportional code, it’s easier to notice where the interesting bits are. It’s easier to notice where the bulk of the work is being done, and focus our review on that. Maybe this is subjective but I posit that the code’s <em>shape</em> looks more like what it is.</p>
<p>We can continue to argue that it’s easier to <em>understand</em> what the work is actually doing, but that’s not a point I want to make. Instead, I want to make a different point: It’s easier to tweak what the code is doing.</p>
<h2 id="opportunity-for-correction">Opportunity for correction</h2>
<p>After writing <em>a lot</em> of code in Python, and writing a moderate amount of code in Go, I noticed an interesting phenomenon: I tweak code very differently in Go.</p>
<p>Programming is a very iterative process. As we implement our solution, our mental model of the problem evolves. We often go back and change some assumptions, tweak our data structures, remove obsolete crutches and stubs, make error handling more robust, and so on.</p>
<p>I noticed that when I wrote Python code, I would often <em>go out of my way</em> to avoid messing up a beautiful terse set of lines by moving logic to places other than where it might naturally belong. I would unconsciously move complexity from where it belongs to a tangential place that made the tweak more complex than it should have been.</p>
<p>For example: Rather than refactoring a try/except block into separate appropriately-layered method calls or multiple try/except phases, I’d introduce a variable to maintain error state that gets handled in a finally block. (<a href="https://github.com/shazow/urllib3/blob/1f53dcaafa0adae65e0902b5a419dd244e853a91/urllib3/connectionpool.py#L656-L668">An example of this pattern in urllib3</a>.)</p>
<p>When I write Go, I noticed that my tweaks almost always go exactly where they naturally belong. <strong>My boilerplate doesn’t stay untouched!</strong></p>
<p>For example: I’ll usually write lots of Go boilerplate in my first iteration, but by the time I look at my code closer to release I notice that it’s no longer looking like boilerplate. By the time I’m done—adding more recovery scenarios, better logging, augmenting errors with more context, handle more edge cases—most of my boilerplate gets a lot more interesting. I did not realize that good boilerplate could be fertile ground for iteration.</p>
<p>When a friend was reading through the <a href="https://github.com/shazow/ssh-chat">source code for ssh-chat</a>, he was surprised that I implemented my own <em>Set</em> type. I explained that while Go doesn’t have a built-in <em>Set,</em> it’s just a few lines of boilerplate to make your own on top of a <em>map</em>. In fact, I noticed that <a href="https://godoc.org/github.com/shazow/ssh-chat/set">my version of Set</a> evolved to be fairly specific to how it was being used. In retrospect, I’m glad that I was tweaking my own implementation iteratively rather than spending time working around whichever limitations a generic library might have had.</p>
<p>I realize now that disproportional code makes me unintentionally dance <em>around</em> it like tiny black holes of complexity. Having <em>proportional</em> boilerplate helps balance out the ease of modifying it with the amount of work that code is actually doing—whether it’s error handling or looping or whatever else.</p>
<h2 id="proportional-code-creates-less-complexity">Proportional code creates less complexity</h2>
<p>I am not claiming that more lines of proportional code is less buggy than fewer lines of disproportional code.</p>
<p>I am not claiming that all boilerplate is good: boilerplate can be disproportional in the other direction, too—where it’s doing far <em>less</em> work than we’d expect.</p>
<p>My claim is: For non-trivial projects, <strong>proportional code is not cumulatively more lines than disproportional code.</strong></p>
<p>When we write disproportional code (or code that varies a lot in proportionality), we shift natural complexity into unnatural places which is an unexpected sacrifice. Our beautiful one-liners are short indeed, but the rest of the project suffers in other places to pay off that purchase.</p>
<p>By the time we’re done iterating and fleshing out our project, we might not be winning as much as we’re losing from getting rid of boilerplate indiscriminately. <em>Proportional</em> boilerplate can help us iterate in a less constrained way.</p>

      ]]></content:encoded></item><item><title>Choosing a new master password</title><link>https://shazow.net/posts/choosing-a-new-master-password/</link><pubDate>Fri, 13 Jan 2017 16:35:23 +0000</pubDate><guid>https://shazow.net/posts/choosing-a-new-master-password/</guid><description>&lt;p>I’ve been meaning to change and improve my master password for years. I &lt;em>knew&lt;/em> the steps but I really wanted someone to spell it out for me anyways, so I’ll spell out the concrete steps for you.&lt;/p>
&lt;p>Popular password managers work by maintaining an encrypted database of many secrets, protected by a combination of the &lt;em>master password&lt;/em> and access to the encrypted database file that is synchronized across your devices. Picking a strong master password is crucial in case one of your devices is stolen or the cloud storage that is hosting your password file is compromised.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>I’ve been meaning to change and improve my master password for years. I <em>knew</em> the steps but I really wanted someone to spell it out for me anyways, so I’ll spell out the concrete steps for you.</p>
<p>Popular password managers work by maintaining an encrypted database of many secrets, protected by a combination of the <em>master password</em> and access to the encrypted database file that is synchronized across your devices. Picking a strong master password is crucial in case one of your devices is stolen or the cloud storage that is hosting your password file is compromised.</p>
<h2 id="1-high-entropy-memorable-pass-phrases">1. High-entropy memorable pass phrases</h2>
<p>I recommend this generator:
<a href="https://github.com/redacted/XKCD-password-generator">https://github.com/redacted/XKCD-password-generator</a></p>
<p>It’s written in Python, it has good defaults, and a sensible built-in word dictionary (65,355 words). You can install it using</p>
<pre tabindex="0"><code>pip install xkcdpass
</code></pre><p>You can tweak the length and formula with various command-line flags. I recommend getting a whole bunch of passwords and visually picking one from the list.</p>
<pre tabindex="0"><code>$ xkcdpass --count=100
jewelweed cruzeiro scrawny preempt edgeways ceramist
vendor sacker someone elevator jeweled croaky
ammonia American bunk bed rhymer velleity drenched
linger largesse plain thoraces withered big deal
Rolland wheelie empanada repossess colic hosteler
....
</code></pre><p>Make sure to pick from the list rather than making up your own, because humans are much worse at being random than we might intuitively think we are.</p>
<h2 id="2-write-it-down">2. Write it down</h2>
<p>Yes, you should write it down — <strong>but make sure it’s written on something that can be reliably destroyed</strong> and won’t be discovered before it’s destroyed. A piece of paper that you can mangle or burn will do the trick. Make sure to keep it safe until you’re ready to destroy it.</p>
<p><strong>Totally optional:</strong> If you’re feeling ambitious, this is the time to augment it a bit by adding a few random symbols and numbers. <strong>Avoid common letter substitutions like</strong> <a href="https://en.wikipedia.org/wiki/Leet"><strong>l33t speak</strong></a> because every password cracking tool automatically tries these out of the box. Pick a couple of random numbers and symbols and litter them in the middle of some of your words.</p>
<p>It’s okay to add randomness to your already-random password, but avoid removing randomness by replacing your random words with ones that you chose on a whim or by simplifying the phrase in general.</p>
<p>If you’re not sure what to do, then just keep the passphrase as it came out from xkcdpass. It’s fine as it is and you don’t want to make it too hard to remember.</p>
<h2 id="3-memorize-and-practice">3. Memorize and practice</h2>
<p>Make a text file with a short message and encrypt it using your new password. I like to use <a href="https://www.tarsnap.com/scrypt.html">scrypt</a> which is available in popular package managers:</p>
<pre tabindex="0"><code>$ brew install scrypt  # or apt-get install scrypt
$ echo &#34;I remembered my password&#34; &gt; pwtest.txt
$ scrypt enc pwtest.txt &gt; pwtest.enc
Please enter passphrase:
...
$ rm pwtest.txt  # Don&#39;t need this anymore
</code></pre><p>Now that we have our encrypted test file, we can practice unlocking it until we memorize the password:</p>
<pre tabindex="0"><code>$ scrypt dec pwtest.enc
Please enter passphrase:
...
I remembered my password
$ scrypt dec pwtest.enc
Please enter passphrase:
...
I remembered my password
$ scrypt dec pwtest.enc
Please enter passphrase:
...
I remembered my password
</code></pre><p>If you prefer to use GPG instead of scrypt:</p>
<pre tabindex="0"><code>$ echo &#34;I remembered my password&#34; &gt; pwtest.txt
$ gpg -c pwtest.txt
Enter passphrase:
...
$ gpg pwtest.txt.gpg
Enter passphrase:
...
I remembered my password
</code></pre><h2 id="4-adopt-the-new-master-password">4. Adopt the new master password</h2>
<p>After a few days of practicing your new password, it’s time to adopt it: Destroy the paper you wrote it onto, change your password manager’s master password, and remember: <strong>Never use your master password anywhere else.</strong></p>
<p>The master password is there to keep your other passwords safe. It should never be used as a password for another account, even if it’s your bank or iCloud or whatever. <strong>The master password is for your password manager</strong>, and you must use your password manager to generate and save passwords for the rest of your accounts.</p>
<p>If you’re not using a password manager yet, I recommend <a href="https://bitwarden.com/">Bitwarden</a>. It&rsquo;s free, open source, has a great paid plan for families and teams, and works on all platforms.</p>
<p>If you insist on something closed source and more expensive but with 5% more polish, <a href="https://1password.com/">1Password</a> is also a good option.</p>
<h2 id="good-security-hygiene">Good security hygiene</h2>
<ul>
<li>
<p>Use a strong randomly-generated passphrase as your master password.</p>
<p><strong>Done, good work.</strong></p>
</li>
<li>
<p>Use a password manager.</p>
<p><strong>Hopefully already done?</strong></p>
</li>
<li>
<p>Enable encryption for all of your devices and services.</p>
<p><strong>Especially portable devices that you can lose, like phones and laptops.</strong></p>
</li>
<li>
<p>Choose to use applications that come with good security defaults out of the box.</p>
<p><strong>Not all security or encryption is made equal:</strong> an application with bad security is like an account with a bad password. Sometimes bad security can be worse than no security if it manages to lull you into false expectations.</p>
</li>
</ul>
<p>You won’t regret good security hygiene, especially as the cyber continues to intensify.</p>

      ]]></content:encoded></item><item><title>Neither self nor this: Receivers in Go</title><link>https://shazow.net/posts/receivers-in-go/</link><pubDate>Tue, 05 Jul 2016 17:49:06 -0400</pubDate><guid>https://shazow.net/posts/receivers-in-go/</guid><description>&lt;p>When getting started with Go, there is a strong temptation to bring baggage from
your previous language. It’s a heuristic which is usually helpful, but sometimes
counter-productive and inevitably results in regret.&lt;/p>
&lt;p>Go does not have classes and objects, but it does have types that we can make
many instances of. Further, we can attach methods to these types and they
kind-of start looking like the classes we’re used to. When we attach a method
to a type, the receiver is the instance of the type for which it was called.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>When getting started with Go, there is a strong temptation to bring baggage from
your previous language. It’s a heuristic which is usually helpful, but sometimes
counter-productive and inevitably results in regret.</p>
<p>Go does not have classes and objects, but it does have types that we can make
many instances of. Further, we can attach methods to these types and they
kind-of start looking like the classes we’re used to. When we attach a method
to a type, the receiver is the instance of the type for which it was called.</p>
<p>Choosing the name of a receiver is not always a trivial task. Should we be lazy
and name them all the same (like <code>this</code> or <code>self</code>)? Or treat them not unlike
local variables by abbreviating the type (like <code>srv</code> to a <code>Server</code> type )? Or
maybe something even more nuanced?</p>
<p>And what are the consequences? How will our code suffer if we choose one
approach over the other? Let&rsquo;s explore.</p>
<h2 id="quick-refresher-on-go-structs">Quick refresher on Go structs</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Server</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">srv</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Close</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">srv</span> <span class="nx">Server</span><span class="p">)</span> <span class="nf">Name</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Every time I start writing somekind of server, it starts out looking like this.
It&rsquo;s a type that holds information about our server, like a <code>net.Listener</code>
socket, and it has a <code>Close()</code> method that shuts down the server. Easy enough.</p>
<p>The <em>receiver</em> of the <code>Close()</code> method is the <code>(srv *Server)</code> part. This says
that inside of the <code>Close()</code> method declaration, the scope will have a <code>srv</code>
variable that is a reference to the instance of the <code>Server</code> that it&rsquo;s being
called on.  That is:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">myServer</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">Server</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="nx">myServer</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span></code></pre></div><p>In this case, the <code>srv</code> that is referenced inside of the <code>myServer.Close()</code> is
effectively the same variable as <code>myServer</code>. They&rsquo;re both references to the same
<code>Server</code> instance.</p>
<h3 id="facts-about-go-methods-and-receivers">Facts about Go methods and receivers</h3>
<p>While we can call a method on a type instance and get the receiver implicitly,
it can also be called explicitly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">myServer</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">Server</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="nx">Server</span><span class="p">.</span><span class="nf">Name</span><span class="p">(</span><span class="nx">myServer</span><span class="p">)</span> <span class="c1">// same as myServer.Name()
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">(</span><span class="o">*</span><span class="nx">Server</span><span class="p">).</span><span class="nf">Close</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">myServer</span><span class="p">)</span> <span class="c1">// same as myServer.Close()
</span></span></span></code></pre></div><p>These functions can be passed around as references just like any other function,
with the implicit receiver or without:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">withReceiver</span> <span class="o">:=</span> <span class="nx">myServer</span><span class="p">.</span><span class="nx">Name</span>
</span></span><span class="line"><span class="cl"><span class="nx">without</span> <span class="o">:=</span> <span class="nx">Server</span><span class="p">.</span><span class="nx">Name</span>
</span></span></code></pre></div><p>Receivers can be passed by reference or passed by value.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">byValue</span> <span class="nx">Server</span><span class="p">)</span> <span class="nf">Hello</span><span class="p">()</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">byReference</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Bye</span><span class="p">()</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
</span></span></code></pre></div><p>This is to illustrate that struct methods in Go are merely thin sugar over
traditional C-style struct helper declarations. An equivalent C method might
look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">server_close</span><span class="p">(</span><span class="n">server</span> <span class="o">*</span><span class="n">srv</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span>
</span></span></code></pre></div><p>Go helps by namespacing the methods and implicitly passing the receiver when
called on an instance, but otherwise there is very little magic going on.</p>
<p>In other languages where <code>this</code> and <code>self</code> is a thing (Python, Ruby, JavaScript,
and so on) it&rsquo;s a much more complicated situation. These are not vanilla local
variables wearing fancy pants. The thing we might expect <code>this</code> to represent
inside of a method could actually represent something very different once
inheritance or metaclasses had their way. In effect, it might not make any sense
to give contextual names like <code>srv</code> rather than <code>self</code> in Python, but it
definitely makes sense in Go.</p>
<h2 id="naming-the-receiver">Naming the receiver</h2>
<p>As we write idiomatic Go code, it&rsquo;s common to use the first letter or a short
abbreviation as the name of the receiver. If the name of the struct is <code>Server</code>,
we&rsquo;ll usually see <code>s</code> or <code>srv</code> or even <code>server</code>. All of these are fine—short is
convenient, but it&rsquo;s more about uniquely identifying the variable in a
consistent way.</p>
<p>Why not <code>self</code> or <code>this</code>? Coming from languages like Python, or Ruby, or
JavaScript, it&rsquo;s tempting to do something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">this</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Close</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>That&rsquo;s one less decision to make every time we declare a struct. <em>All</em> of our
methods could use the same receiver. Any time we see <code>this</code> in the code, we&rsquo;ll
<em>know</em> that we&rsquo;re talking about the receiver, not some random local variable. It
will be GREAT!.. or will it?</p>
<p>What if we refactor the code and <code>this</code> is no longer referring to the same thing
as before? And are we giving up valuable semantic meaning?</p>
<h3 id="reshaping-our-code">Reshaping our code</h3>
<p>Eventually we&rsquo;ll need to refactor some of our code: Take a chunk of code that is
already functional and put it in another context where it allows for more
flexibility towards a higher-level goal.</p>
<p>For example, consider moving pieces of code <em>between</em> levels abstractions or
from higher levels of abstraction into a lower level. Imagine taking this
snippet from a higher-level container like <code>Room</code> which holds groups of users in
a server, and moving it up or down one level:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">this</span> <span class="o">*</span><span class="nx">Room</span><span class="p">)</span> <span class="nf">Announce</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">srv</span> <span class="o">:=</span> <span class="nx">this</span><span class="p">.</span><span class="nf">Server</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">srv</span><span class="p">.</span><span class="nf">Clients</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Send announcement to all clients about a new room
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">c</span><span class="p">.</span><span class="nf">Send</span><span class="p">(</span><span class="nx">srv</span><span class="p">.</span><span class="nf">RenderAnnouncement</span><span class="p">(</span><span class="nx">this</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Moved between...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">this</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">AddRoom</span><span class="p">(</span><span class="nx">room</span> <span class="o">*</span><span class="nx">Room</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">this</span><span class="p">.</span><span class="nf">Clients</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Send announcement to all clients about a new room
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">c</span><span class="p">.</span><span class="nf">Send</span><span class="p">(</span><span class="nx">this</span><span class="p">.</span><span class="nf">RenderAnnouncement</span><span class="p">(</span><span class="nx">room</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>When using <code>this</code>, there is confusion about whether we&rsquo;re referring to the
server or the room as we&rsquo;re moving the code between.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">-       c.Send(this.RenderAnnouncement(room))
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+       c.Send(srv.RenderAnnouncement(this))
</span></span></span></code></pre></div><p>Refactoring this kind of code produce some bugs that the compiler will hopefully
catch (or maybe not, if the interfaces happen to be compatible). Even bugs
aside, having to edit all the little innards does make moving code around more
tedious.</p>
<p>Moving across levels of abstraction is a great example of when consistently
well-named receivers make a huge difference:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">room</span> <span class="o">*</span><span class="nx">Room</span><span class="p">)</span> <span class="nf">Announce</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">srv</span> <span class="o">:=</span> <span class="nx">room</span><span class="p">.</span><span class="nf">Server</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">srv</span><span class="p">.</span><span class="nf">Clients</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Send announcement to all clients about a new room
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">c</span><span class="p">.</span><span class="nf">Send</span><span class="p">(</span><span class="nx">srv</span><span class="p">.</span><span class="nf">RenderAnnouncement</span><span class="p">(</span><span class="nx">room</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Moved between...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">srv</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">AddRoom</span><span class="p">(</span><span class="nx">room</span> <span class="o">*</span><span class="nx">Room</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">c</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">srv</span><span class="p">.</span><span class="nf">Clients</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Send announcement to all clients about a new room
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nx">c</span><span class="p">.</span><span class="nf">Send</span><span class="p">(</span><span class="nx">srv</span><span class="p">.</span><span class="nf">RenderAnnouncement</span><span class="p">(</span><span class="nx">room</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This is a great little pattern to keep everything working despite moving between
layers of abstraction. Note how the inner code stays identical and all we&rsquo;re
doing is sometimes adding a little extra context outside of it.</p>
<p>As projects mature, this kind of refactoring happens surprisingly often. We&rsquo;re
talking about just one line in this example, but the same applies for larger
chunks too.</p>
<p>The suggested strategy for naming Go receivers is the same strategy for naming
normal local variables. If they&rsquo;re named similarly, then these code blocks can
be moved wholesale between layers of abstraction with minimal hassle and helps
us avoid careless bugs.</p>
<p>By naming receivers as <code>this</code> or <code>self</code>, we&rsquo;re actually making receivers
<em>special</em> in a way that is counter-productive. Imagine naming every local
variable with the same name, all the time, regardless of what it represents? A
scary thought.</p>
<h2 id="advanced-naming-technique">Advanced naming technique</h2>
<p>If we agree that contextually named receivers are meaningful, then maybe we can
utilize this opportunity for an even greater advantage.</p>
<p>What if we named our receivers based on the interface that they&rsquo;re implementing
(if any)? Let&rsquo;s say we add <code>io.Writer</code> and <code>io.Reader</code> interfaces to our Server:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">w</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Write</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Send p to all clients
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Receive data from all clients
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>Maybe we also want to add the <code>http.Handler</code> interface to provide a dashboard
for our server.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">handler</span> <span class="o">*</span><span class="nx">Server</span><span class="p">)</span> <span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Render dashboard
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>There are a few benefits to doing it this way:</p>
<ul>
<li>
<p>The receivers enhance the self-documenting nature of our code. It becomes
clearer which interface each method is attempting to implement.</p>
</li>
<li>
<p>If we were implementing these wrappers outside of the <code>Server</code> struct, we
would likely be using similarly named variabls for intermediate code. By
naming the receiver in a corresponding way, it makes it easier to move the
code inline without changing much.</p>
</li>
<li>
<p>As we add interface-specific functionality, it&rsquo;s likely that we&rsquo;ll need to add
more fields to our struct to manage various related state. The code can look
more meaningful when a read buffer is being accessed from a <code>r</code> receiver to
imply that its purpose is specifically for this functionality rather than it
being a more general buffer for the server as a whole.</p>
</li>
</ul>
<h2 id="name-of-the-receiver">Name of the Receiver</h2>
<p>Carefully naming our receivers can have lots of tangible benefits, especially as
our project grows and code gets moved around. It can make our inner method code
much more readable without needing to be aware of which struct it&rsquo;s embedded
into. It can even add an opportunity to indicate higher-level layout of our
struct&rsquo;s interface implementation.</p>
<p>Picking a fixed name for all receivers like <code>self</code> can have negative effects
like mixing up context when code gets moved around. It removes a decision during
writing, but the cost creeps up when we go back to read the code or refactor it.</p>
<p>Go forth and give your receivers the names they deserve.</p>

      ]]></content:encoded></item><item><title>See Python, See Python Go, Go Python Go @ PyCon 2016</title><link>https://shazow.net/posts/see-python-see-python-go-go-python-go/</link><pubDate>Wed, 01 Jun 2016 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/see-python-see-python-go-go-python-go/</guid><description>&lt;p>...&lt;/p></description></item><item><title>In VR, Would a Rose Smell Sweeter?</title><link>https://shazow.net/posts/in-vr-would-a-rose-smell-sweeter/</link><pubDate>Thu, 14 Jan 2016 20:40:50 +0000</pubDate><guid>https://shazow.net/posts/in-vr-would-a-rose-smell-sweeter/</guid><description>&lt;p>In a few months, the first iteration of modern consumer virtual reality headsets are going to start shipping. A lot is rapidly changing and new applications are being discovered every week.&lt;/p>
&lt;p>I present three stories that use VR technology to push our crafts beyond what was possible. Each story is focused around an amazing short video that is very much worth watching.&lt;/p>
&lt;h2 id="this-is-not-why-_i_-became-an-actor">“This is not why &lt;em>I&lt;/em> became an actor.”&lt;/h2>
&lt;p>On the set of The Hobbit, Sir Ian McKellen broke down in tears after hours of delivering his lines as Gandalf to a handful of photographs floating amidst a sea of green.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>In a few months, the first iteration of modern consumer virtual reality headsets are going to start shipping. A lot is rapidly changing and new applications are being discovered every week.</p>
<p>I present three stories that use VR technology to push our crafts beyond what was possible. Each story is focused around an amazing short video that is very much worth watching.</p>
<h2 id="this-is-not-why-_i_-became-an-actor">“This is not why <em>I</em> became an actor.”</h2>
<p>On the set of The Hobbit, Sir Ian McKellen broke down in tears after hours of delivering his lines as Gandalf to a handful of photographs floating amidst a sea of green.</p>
<p><img src="/posts/in-vr-would-a-rose-smell-sweeter/images/1.jpeg#layoutTextWidth" alt="image"></p>
<p>In <a href="http://www.contactmusic.com/ian-mckellen/news/ian-mckellen-broke-down-on-the-hobbit-set_3378939">an interview with Contact Music</a>, he recalled:</p>
<blockquote>
<p>In order to shoot the dwarves and a large Gandalf, we couldn’t be in the same set. All I had for company was 13 photographs of the dwarves on top of stands with little lights — whoever’s talking flashes up.&gt; Pretending you’re with 13 other people when you’re on your own, it stretches your technical ability to the absolute limits.&gt; I cried, actually. I cried. Then I said out loud, ‘This is not why I became an actor’. Unfortunately the microphone was on and the whole studio heard.</p>
</blockquote>
<p>Today, we’re entering a different world. One where actors can be immersed in the virtual world they’re depicting <em>as they’re depicting it.</em></p>
<p>Cloudhead Games is working with actor <a href="http://www.imdb.com/name/nm0396406/">Adrian Hough</a> to <a href="http://www.thegallerygame.com/blog/storyinvr/">record the motion captured acting end-to-end for the first episode of The Gallery</a>, right inside the world itself as it’s being acted out:</p>
<p>As Valve’s Lighthouse Tracking system becomes available later this year, trackable peripherals and body attachments will bring a world of consumer motion capture. Someday, it might even be the default for most VR experiences. You’ll raise your hand, wiggle your fingers, and take that precious swing to feel haptics sharply vibrate in your palm as you slap your friend across the continent. <a href="https://webcache.googleusercontent.com/search?q=cache:kbNowrMCkzIJ:bash.org/%3F4281+&amp;cd=1&amp;hl=en&amp;ct=clnk&amp;gl=us">The future is here</a>.</p>
<h2 id="what-is-this-amazing-new-world">“What is this amazing new world?”</h2>
<p>Glen Keane is an animator who worked at Disney for 38 years, he helped create many of the characters we all take for granted from our childhood.</p>
<figure><img src="/posts/in-vr-would-a-rose-smell-sweeter/images/2.jpeg"><figcaption>
      <h4>Glenn Keane illustrations</h4>
    </figcaption>
</figure>

<p>In collaboration with the Future of StoryTelling Summit, Keane put on an HTC Vive headset and picked up the wireless controllers. He entered a new world:</p>
<blockquote>
<p>When I draw in virtual reality, I draw all the characters in real life size. They are that size in my imagination.</p>
</blockquote>
<p>This world is called <a href="http://www.tiltbrush.com/">Tilt Brush</a>, a VR painting application created by Google.</p>
<p>For years we’ve been fed images of <a href="http://www.dezeen.com/2015/04/27/virtual-reality-architecture-more-powerful-cocaine-oculus-rift-ty-hedfan-olivier-demangel-ivr-nation/">architects</a> and <a href="http://www.cnn.com/2016/01/07/health/google-cardboard-baby-saved/">surgeons</a> using virtual or augmented reality to feel what it’s like to be <em>inside</em> their building or patient. But what about us? What about our inner child unleashed inside a life-sized world of our own design, not just staring at it from the distant abstraction of a flat display.</p>
<h2 id="your-crappy-pc-is-the-biggest-barrier-to-vr-adoption">“Your crappy PC is the biggest barrier to [VR] adoption.”</h2>
<p>Palmer Luckey, founder of now-Facebook’s Oculus RV, hosted a marathon of an <a href="https://www.reddit.com/r/pcmasterrace/comments/40ea0x/i_am_palmer_luckey_founder_of_oculus_and_designer/">IAMA on /r/pcmasterrace</a> last week. One recurring topic was the recommended hardware requirements for running the Oculus Rift, specifically the “<em>NVIDIA GTX 970 / AMD 290 equivalent or greater” —</em> a $350 investment just for the video card, or more if you’re shooting for above the bare minimum.</p>
<p>In modern gaming, <em>1080p</em> resolution is considered passable. At 1920x1080, these 2,073,600 pixels are what you’d expect on a PS4 or XBox 360 rendered about 30 times each second. On a PC, it’s common to see higher resolutions pushing 60fps. What happens if some big explosions break out and the frame-rate drops to 45? Not much. If it drops below 20fps, you might notice and grumble a bit until things calm down again.</p>
<p>The first iteration of VR is going to be a more expensive beast: Two camera positions need to be rendered for every frame—one for each eye—<em>90 times per second.</em> Not to mention that the display panels are higher resolution, probably around 1512x1680 per eye or 5,080,320 pixels total. And what happens if you swing your head and the frame-rate drops to 40 fps for a few moments? Nausea. You’re done.</p>
<p>Today, we’re talking about 62 million pixels/sec rendered on latest-generation consoles compared to <a href="http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf">at least 378 million pixels/sec rendered for nausea-free VR</a>.</p>
<p>Sounds gloomy, but don’t fret—the forecast is sunny: If we can track where our eyes are looking at inside of the headset, we can reduce the full-quality rendering to less than a quarter of the resolution. This is called <em>Foveated Rendering</em>, and a German startup already has a working prototype:</p>
<p>What does this mean?</p>
<p>Once Foveated Rendering reaches consumers, we might actually see higher graphics quality <em>and</em> lower hardware requirements from VR than traditional flat-display technology.</p>
<p>I can’t wait.</p>

      ]]></content:encoded></item><item><title>How I design JSON API responses</title><link>https://shazow.net/posts/how-i-design-json-api-responses/</link><pubDate>Tue, 17 Nov 2015 21:34:42 +0000</pubDate><guid>https://shazow.net/posts/how-i-design-json-api-responses/</guid><description>&lt;p>The goal of designing a public API response layout is to balance ease of use for consumers with commitment of stability from the provider. We can bolt on all kinds of crazy metadata and embedded values that we’ll regret having to maintain years later, like complex pagination schemes that don’t scale for evolving domain spaces or when we’re finally &lt;em>Web Scale&lt;/em> and wasting millions on extra bandwidth from that emoji soup we thought would be hilarious to wrap each entry with.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>The goal of designing a public API response layout is to balance ease of use for consumers with commitment of stability from the provider. We can bolt on all kinds of crazy metadata and embedded values that we’ll regret having to maintain years later, like complex pagination schemes that don’t scale for evolving domain spaces or when we’re finally <em>Web Scale</em> and wasting millions on extra bandwidth from that emoji soup we thought would be hilarious to wrap each entry with.</p>
<p>Let’s keep it simple and direct.</p>
<pre tabindex="0"><code>{
    &#34;status&#34;: &#34;ok&#34;,
    &#34;code&#34;: 200,
    &#34;messages&#34;: [],
    &#34;result&#34;: {
        &#34;user&#34;: {
            &#34;id&#34;: 123,
            &#34;name&#34;: &#34;shazow&#34;
        }
    }
}
</code></pre><h3 id="result">result</h3>
<p>The hard-coded name of “result” could be anything—like <em>payload</em> or <em>response</em> or whatever sounds sensible—but the keys inside are important. Each key helps us double-check our expectations of what is inside:</p>
<pre tabindex="0"><code>data[&#34;result&#34;][&#34;user&#34;] == (a user object)
data[&#34;result&#34;][&#34;users&#34;] == (a list of user objects)
data[&#34;result&#34;][&#34;post&#34;] == (a post object)
</code></pre><p>This distinction is a valuable tool for the consumer. If I’m skimming the docs and expecting some API endpoint to return a <em>user</em> object but instead it returns a <em>member</em> object which is similar but subtly different, then trying to access the <em>user</em> key will reveal the problem immediately.</p>
<p>Imagine an alternate scenario, where we return each type inlined in the result:</p>
<pre tabindex="0"><code>data[&#34;result&#34;] == (a user object)
data[&#34;result&#34;] == (a list of user objects)
data[&#34;result&#34;] == (a post object)
</code></pre><p>My code might seem like it’s working but it might seem like it by accident. I might treat a <em>post</em> id as a <em>user</em> id and get all kinds of weird results without realizing why. Maybe the inline result includes a handy “type” field, but who knows if I’ll bother checking against it every single time.</p>
<p>Bonus feature: Having the result objects keyed by their types allows you to return multiple things in a single result. Maybe it’s most efficient for the consumer to return a post <em>and</em> a user in the same response? It’s nice to keep our options open.</p>
<h3 id="status">status</h3>
<p>As a consumer, when I get a response from this API, first thing I do is check the status value. Is it “ok”? Great, carry on as expected. Is it an “error”? I’ll need to handle it.</p>
<p>This key only has two possible values, so it’s nice and simple for the consumer’s code.</p>
<h3 id="code">code</h3>
<p>When I get an error, this is where I’ll go to differentiate it and maybe handle it programmatically. We can use HTTP-like codes, or just incremental codes, or whatever makes us happy as long as it’s well-documented. Errors should be obvious and/or well-documented with what each endpoint could return and how we should handle it.</p>
<p>Why not just use HTTP response codes instead of embedding a <em>code</em> value in our JSON? Three reasons:</p>
<ol>
<li>Consumers don’t like checking response codes, it’s a lazy thing. All of my code is already dealing with JSON values so why not one more. Also some HTTP libraries make it somewhat difficult to get at the value.</li>
<li>HTTP response codes are unreliable. Some poorly-implemented proxy code could override the code, or your cache will kick in and serve that one buggy response which might be inconsistent with the values inside.</li>
<li>No reason to tightly couple with your transport protocol. By all means, mirror your codes in HTTP but if you include it in the encoded response then you can just as easily switch the API to straight-up TCP or even something crazy like <a href="https://medium.com/@shazow/ssh-how-does-it-even-9e43586e4ffc">an SSH RPC API</a>!</li>
</ol>
<p>We could skip the <em>status</em> field by saying “code 200 is a success” or similar, but I like the binary nature of <em>status.</em> The API is less intimidating if I don’t need to learn about what which codes mean until I’m diving into actually handling specific errors.</p>
<h3 id="messages">messages</h3>
<p>This one is purely for debugging or informational purposes. If there’s an error, then we’ll get some messages here telling us what went wrong—along the lines of <em>“user id does not exist: 4”</em></p>
<p>We could add some user-friendly success messages in there, too—like <em>“post created: My JSON API response.”</em> I’ll often use the same API for my frontend client-side handlers as I do for backend stuff, so I can flash these messages as they appear.</p>
<p>Some APIs call this field <em>errors</em>, but I like that it can be used for non-errors.</p>
<h3 id="extra-stuff">Extra stuff?</h3>
<ul>
<li>It can be fun to include a <em>stats</em> object with performance information.</li>
<li>A <em>pretty</em> request parameter should indent our JSON and sort the keys, very handy for being human-readable and for comparing test output in a deterministic way.</li>
<li>Pagination can be done with something like data[“result”][“offset”] and the offset value passed back in with the next query. This can be an arbitrary token or a page number. Not all responses make sense to paginate, so I prefer to include it as part of the result when it makes sense rather than making it a root-level value that is often ignored.</li>
</ul>
<h2 id="patterns-avoided">Patterns Avoided</h2>
<p>There are lots of complicated standards that have evolved, like <a href="http://jsonapi.org">jsonapi.org</a> and <a href="https://en.wikipedia.org/wiki/HATEOAS">HATEOAS</a> (remember SOAP?). Some of those ideas can be safely avoided, especially if we’re not building ENTERPRISE SOFTWARE (TM).</p>
<h3 id="api-links">api links</h3>
<pre tabindex="0"><code>    ...
    &#34;user&#34;: {
        &#34;id&#34;: &#34;1&#34;,
        &#34;name&#34;: &#34;shazow&#34;,
        &#34;links&#34;: {
            &#34;self&#34;: &#34;https://example.com/api/v1/user/1.json&#34;,
            &#34;posts&#34;: &#34;https://example.com/api/v1/user/1/posts.json&#34;
        }
    }
</code></pre><p>Some APIs include a pre-generated API request URLs, such as for pagination or querying sub-relationships. This never made any sense to me. To get to this response, my consumer code already needs to know how to compose the URL in the first place. Suddenly I’m supposed to switch from using my URL-building code to using pre-built URLs returned in the response? This leads to all kinds of bugs:</p>
<ol>
<li>More branching in the code, toggling between my own URL-building logic and pre-built URL logic.</li>
<li>API response might be normalizing URLs in a way that is different than I intended. Or maybe I’m accessing an API through a proxy like <a href="https://www.runscope.com">Runscope</a>? <em>“Ugh why is my first request working but the rest fail, WHY??! 😡🐚♨️”</em></li>
</ol>
<p>This also breaks if you’re trying to avoid coupling with HTTP as a transport, or if you need to multipart-encode a large request and url-encoded variables don’t get merged.</p>
<p>On the other hand, including URLs for downloading or frontends to navigate to is fine: Images, browser links, whatever. We’re talking strictly about API endpoints here.</p>
<h3 id="generic-data-lists-with-types">generic data lists with types</h3>
<pre tabindex="0"><code>{
    &#34;data&#34;: [
        {
            &#34;id&#34;: &#34;1&#34;,
            &#34;name&#34;: &#34;shazow&#34;,
            &#34;type&#34;: &#34;user&#34;
        }
    ]
}
</code></pre><p>It’s not any more expressive than our <em>result</em> layout above, and we’d be requiring our consumer to check the <em>type</em> field for every object they touch to make sure they’re not mistreating it. They won’t. Bugs will ensue.</p>
<h2 id="other-cans-of-worms">Other cans of worms</h2>
<p>I have <em>feelings</em> on other API-related topics like REST vs RPC, language-native SDK wrappers, versioning, transport protocols, XSS security strategies, and more.</p>
<p>Let me know what interests you at <a href="https://twitter.com/shazow">twitter.com/shazow</a>.</p>

      ]]></content:encoded></item><item><title>Why aren’t we using SSH for everything?</title><link>https://shazow.net/posts/ssh-how-does-it-even/</link><pubDate>Fri, 02 Jan 2015 20:56:34 +0000</pubDate><guid>https://shazow.net/posts/ssh-how-does-it-even/</guid><description>&lt;blockquote>
&lt;p>&lt;em>SSH, how does it even?&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;p>A few weeks ago, I wrote &lt;a href="https://github.com/shazow/ssh-chat">ssh-chat&lt;/a>.&lt;/p>
&lt;blockquote class="twitter-tweet">&lt;p lang="und" dir="ltr">ssh &lt;a href="http://t.co/E7Ilc0B0BC">http://t.co/E7Ilc0B0BC&lt;/a> &lt;a href="http://t.co/CqYBR1WYO4">pic.twitter.com/CqYBR1WYO4&lt;/a>&lt;/p>&amp;mdash; Andrey 🦃 Petrov (@shazow) &lt;a href="https://twitter.com/shazow/status/543852069417787392?ref_src=twsrc%5Etfw">December 13, 2014&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>


&lt;p>The idea is simple: You open your terminal and type,&lt;/p>
&lt;pre tabindex="0">&lt;code>$ ssh chat.shazow.net
&lt;/code>&lt;/pre>&lt;p>Unlike many others, you might stop yourself before typing “ls” and notice — that’s no shell, it’s a chat room!&lt;/p>
&lt;p>While the little details sink in, it dawns on you that there is something extra-special going on in here.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <blockquote>
<p><em>SSH, how does it even?</em></p>
</blockquote>
<p>A few weeks ago, I wrote <a href="https://github.com/shazow/ssh-chat">ssh-chat</a>.</p>
<blockquote class="twitter-tweet"><p lang="und" dir="ltr">ssh <a href="http://t.co/E7Ilc0B0BC">http://t.co/E7Ilc0B0BC</a> <a href="http://t.co/CqYBR1WYO4">pic.twitter.com/CqYBR1WYO4</a></p>&mdash; Andrey 🦃 Petrov (@shazow) <a href="https://twitter.com/shazow/status/543852069417787392?ref_src=twsrc%5Etfw">December 13, 2014</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<p>The idea is simple: You open your terminal and type,</p>
<pre tabindex="0"><code>$ ssh chat.shazow.net
</code></pre><p>Unlike many others, you might stop yourself before typing “ls” and notice — that’s no shell, it’s a chat room!</p>
<p>While the little details sink in, it dawns on you that there is something extra-special going on in here.</p>
<h2 id="ssh-knows-your-username">SSH knows your username</h2>
<p>When you ssh into a server, your client shares several environment variables as inputs to the server, among them is the $USER variable. You can overwrite this, of course, by specifying the user you’re connecting as such:</p>
<pre tabindex="0"><code>$ ssh neo@chat.shazow.net
</code></pre><p>Well look at that, you’re the one. So special. What else can we mess with? By default, the server gets your $TERM variable too.</p>
<pre tabindex="0"><code>$ TERM=inator ssh chat.shazow.net
</code></pre><p>If ssh-chat was smart, it would recognize that your custom terminal might not support colours and skip sending those extra formatting characters.</p>
<p>You can even push your own environment variables with a <em>SendEnv</em> option flag, but we won’t get into that.</p>
<h2 id="ssh-validates-against-your-key-pair">SSH validates against your key pair</h2>
<p>There are several supported authentication methods in SSH: <em>none</em>, <em>password, keyboard-interactive,</em> and <em>publickey</em>. They each have interesting properties, but the last one is especially handy.</p>
<p>When your SSH client connects to a server, it negotiates an acceptable authentication method that both support (typically the reverse order of the above). If you’ve specified the identity flag or have some keys lying around in your <em>~/.ssh/</em> directory, your client will offer up some public keys to authenticate against. If the server recognizes one of those keys, such as if they’re listed in <em>authorized_keys</em>, then a secure handshake will begin verifying that you hold the private key to that public key but without revealing what the private key is. In the process, the client and server securely agree on a temporary symmetric <em>session key</em> to encrypt the communication with.</p>
<p>What does this mean? It means that SSH has authentication built into the protocol. <strong>When you join ssh-chat, not only do I know who you <em>claim</em> to be, but I can also permanently and securely attach an identity to your connection without any user intervention on your part.</strong> No signup forms, no clicking on links in your email, no fancy mobile apps.</p>
<p>A future version of ssh-chat will allow you to create a permanent account which is validated against your key pair, and these permanent accounts might have all kinds of special features like username ownership, always-online presence, and push notifications over Pushover or email.</p>
<h3 id="ssh-connections-are-encrypted">SSH connections are encrypted</h3>
<p>The server uses the same kind of key pair as a client would. When you connect to a new SSH host, you get a message that presents a “key fingerprint” for you to validate. The fingerprint is the hex of a hash of the server’s public key.</p>
<p><img src="/posts/ssh-how-does-it-even/images/1.png#layoutTextWidth" alt="image"></p>
<p>What does it mean if you try to connect to chat.shazow.net and you see a <em>different</em> fingerprint hash? You’re being <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">man-in-the-middle</a>’d.</p>
<p>Your local neighbourhood clandestine security agency could make an SSH server that just acts as a proxy in front of another SSH server you frequent (using something like <a href="http://linux.die.net/man/8/sshmitm">sshmitm</a>) and log everything that is going on while passing it through. Fortunately, as long as the proxy doesn’t have the original server’s private key, then the key fingerprint will be different.</p>
<p>Once you accept a fingerprint, it will be added to your <em>~/.ssh/known_hosts</em> where it will be <em>pinned</em> to that host. This means if the key for the host ever changes, you’ll be greeted with this appropriately-scary message:</p>
<p><img src="/posts/ssh-how-does-it-even/images/2.png#layoutTextWidth" alt="image"></p>
<p>A host you’ve connected to previously is advertising a different public key than it did before. If you can’t account for this change (maybe you launched a new VPS on the same IP address as before and it generated a fresh SSH key pair?) then it’s worth being worried. Try connecting to this host from another network, see if the problem persists — if not, then someone is hijacking your local connection rather than the server’s connection.</p>
<h2 id="ssh-supports-multiplexing">SSH supports multiplexing</h2>
<p>When your client connects to a server, it opens a <em>channel</em> where it requests a specific feature. There are many fun things your client can request like <em>pty-req</em> (a pseudo-terminal), <em>exec</em> (run command), or even <em>tcpip-forward</em> (port forwarding). <a href="http://www.ietf.org/rfc/rfc4254.txt">There are many others</a>, and there is nothing stopping you from inventing your own type for a custom client/server implementation. Maybe we’ll see a <em>chat</em> channel someday?</p>
<p>The best part is that you can do all of these things concurrently: Start port forwarding while opening a shell while having some command run in the background.</p>
<p>Once your pipeline is opened, you can send <em>more</em> commands within it. When your client opens a <em>pty-req</em>, it sends event updates such as <em>window-change</em> whenever your terminal size changes.</p>
<h2 id="ssh-is-ubiquitous">SSH is ubiquitous</h2>
<p>“Is it mobile-friendly?” you may joke, but it is! Every platform you can imagine, there is an SSH client available, including iOS, Android, even Windows! OSX and every Linux distro ships with a client. There are even <a href="https://chrome.google.com/webstore/detail/secure-shell/pnhechapfaindjhompbnflcldabbghjo?hl=en">browser extension SSH clients</a>.</p>
<p>SSH is one of the most accessible secure protocols ever, second only to HTTPS of course.</p>
<h2 id="ssh-sounds-awesome-and-familiar">SSH sounds awesome, and familiar…</h2>
<p>Let’s see what we have so far: Binary protocol, mandatory encryption, key pinning, multiplexing, compression (yes, it does that too).</p>
<p>Aren’t these the key features for why we invented HTTP/2?</p>
<p>Admittedly, SSH is missing some pieces. It’s lacking a notion of virtual hosts, or being able to serve different endpoints on different hostnames from a single IP address.</p>
<p>On the other hand, SSH <em>does</em> have several cool features over HTTP/2 though, like built-in client authentication which removes the need for registration and remembering extra passwords.</p>
<h2 id="more-factlets-to-fill-your-stockings">More factlets to fill your stockings</h2>
<ul>
<li>SSH server and client specification is fairly symmetric. Per the protocol, most of what the client can ask of a server, a server <em>could</em> ask of the client. This includes things like run commands, but mainstream clients don’t implement this (as is recommended against in the specification).</li>
<li>Every keystroke is sent over the TCP connection. This is why you might notice lag in your typing.</li>
<li>To see what your OpenSSH client is doing, use -v to enable verbose debugging output. Use another -v to see per-keystroke debugging, and another -v to further increase the silliness.</li>
<li>There is a protocol called <a href="https://en.wikipedia.org/wiki/Mosh_%28software%29">MOSH</a> which uses SSH to bootstrap but uses client-side predictive rendering and a UDP state synchronization protocol to remove the effects of latency. I wish there were more third-party implementations of it.</li>
<li>Since SSH supports port forwarding and a SOCKS proxy interface, you can build a VPN on top of it by using something like <a href="https://github.com/apenwarr/sshuttle">sshuttle</a>.</li>
<li>SSH can authenticate using a certificate authority scheme, similar to x.509 certificates used in TLS. Also, many clients can verify server fingerprints against an <a href="https://tools.ietf.org/html/rfc4255"><em>SSHFP</em> DNS</a> entry.</li>
</ul>
<h2 id="some-provocative-ssh-ideas">Some provocative SSH ideas</h2>
<p>Chat over SSH was fun, but that’s just the tip of what we can come up with.</p>
<h4 id="multi-user-dungeon-mud"><strong>Multi User Dungeon (MUD)</strong></h4>
<p>Someday, you’ll be able to ssh into <em>mud.shazow.net</em> and you’ll get a little ASCII RPG world to explore. Not yet, but it just might happen.</p>
<h4 id="distributed-hash-table-dht"><strong>Distributed Hash Table (DHT)</strong></h4>
<p>This gets technical but the possibilities are striking… <blockquote class="twitter-tweet"><p lang="en" dir="ltr">Imagine S/Kademlia DHT implemented atop SSH: <a href="http://t.co/poY2Ibu30W">pic.twitter.com/poY2Ibu30W</a></p>&mdash; Andrey 🦃 Petrov (@shazow) <a href="https://twitter.com/shazow/status/549348566972370944?ref_src=twsrc%5Etfw">December 28, 2014</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

</p>
<h4 id="programmatic-data-streams"><strong>Programmatic Data Streams</strong></h4>
<p>Or better yet, ZeroMQ-style sockets with proper security and encryption? Check out <a href="https://github.com/progrium/duplex">Jeff Lindsay’s Duplex</a>. Still a proof of concept, but lots of really cool demos.</p>
<h4 id="rpc-api"><strong>RPC API</strong></h4>
<p>SSH’s built-in authentication and encryption makes it <em>really</em> convenient for things like APIs. No complicated OAuth2 handshakes or HMACs and signatures.</p>
<pre tabindex="0"><code>ssh api.example.com multiply a=4 b=5
</code></pre><p>Someday we’ll have good libraries which make connecting over SSH just as easy as HTTP. At that point, your code will look exactly like today’s run-of-the-mill REST API, but use SSH underneath.</p>
<p>Either way, the days of <em>curl</em> examples in API docs would be behind us.</p>
<h4 id="file-server"><strong>File Server</strong></h4>
<p>If we have an RPC API, why not serve static files while we’re at it?</p>
<pre tabindex="0"><code>ssh static.example.com get /images/header.png
</code></pre><p>Remember, SSH supports persistent connections just as well, so your browser could sit there connected to an SSH channel named <em>get</em> for the host and send concurrent <em>get</em> requests for assets. We could even implement ETAGs, and whatever else.</p>
<h4 id="and-finally-http"><strong>And finally, HTTP</strong></h4>
<p>At this point, there’s no reason we couldn’t build a version of HTTP/1 or HTTP/2 on top of SSH. Let’s add a <em>header</em> channel to specify things like <em>Host</em> for virtual host support, throw in some <em>Cookie</em> headers too. Want to add some method verbs? Why not, let’s make a bunch of channels like <em>post</em> or maybe <em>http-post</em> if we want to be polite.</p>
<h2 id="why-arent-we-using-ssh-for-everything">Why aren’t we using SSH for everything?</h2>
<p>Great question.</p>

      ]]></content:encoded></item><item><title>Urllib3, Stripe, and Open Source Grants</title><link>https://shazow.net/posts/urllib3-stripe-and-open-source-grants/</link><pubDate>Mon, 07 Jul 2014 21:01:26 +0000</pubDate><guid>https://shazow.net/posts/urllib3-stripe-and-open-source-grants/</guid><description>&lt;h3 id="helping-the-future-come-a-little-sooner">Helping the future come a little sooner&lt;/h3>
&lt;p>Six years ago, I built &lt;a href="https://github.com/shazow/urllib3">a better Python http library&lt;/a>. It had then-novel things like connection pooling, thread-safety, multipart encoding, and more. Today, every Python user depends on it.&lt;/p>
&lt;p>Maintaining something for six years on my own time is not easy, even with &lt;a href="https://medium.com/@shazow/how-to-maintain-a-successful-open-source-project-aaa2a5437d3a">a slew of coping strategies&lt;/a>. A few hours here and there, every week, and slowly it chugs along—largely thanks to contributors undertaking the bigger tasks that I can’t knock off in a quick afternoon.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <h3 id="helping-the-future-come-a-little-sooner">Helping the future come a little sooner</h3>
<p>Six years ago, I built <a href="https://github.com/shazow/urllib3">a better Python http library</a>. It had then-novel things like connection pooling, thread-safety, multipart encoding, and more. Today, every Python user depends on it.</p>
<p>Maintaining something for six years on my own time is not easy, even with <a href="https://medium.com/@shazow/how-to-maintain-a-successful-open-source-project-aaa2a5437d3a">a slew of coping strategies</a>. A few hours here and there, every week, and slowly it chugs along—largely thanks to contributors undertaking the bigger tasks that I can’t knock off in a quick afternoon.</p>
<p>Last week completes my two week grant from <a href="https://stripe.com/">Stripe</a> to work on urllib3 full time, and I’d like to declare that these weeks were a huge success.</p>
<h3 id="tldr">tl;dr:</h3>
<ul>
<li>$3,750 for two weeks of full time work at Stripe.</li>
<li>51 files changed, 1809 insertions(+), 633 deletions(-).</li>
<li>10 merged pull requests, 26 closed issues, 6 new issues.</li>
<li><a href="http://pypi.python.org/pypi/urllib3#changes">One major release</a> (v1.9).</li>
<li>~6 months worth of progress in two weeks.</li>
<li>Tech companies: More like this, please.</li>
</ul>
<h2 id="stripe-open-source-retreat">Stripe Open-Source Retreat</h2>
<p>In April, Stripe <a href="https://stripe.com/blog/stripe-open-source-retreat">announced the intent to sponsor a few open source projects</a> for 3 months of full-time work at the office.</p>
<p>I drafted an email proposing a 2 week arrangement for urllib3 instead. I wanted a shorter term because I didn’t want to fully relocate my life to The City, and I felt that urllib3's backlog would get the most impact out of a few weeks worth of work without hitting diminishing returns.</p>
<p><a href="https://twitter.com/thegdb">Greg Brockman</a> responded enthusiastically and worked out the ideal structure for me. Fast forward a couple of months, and the grant was official. We cut the $7,500/mo in half and settled on $3,750 for 2 weeks.</p>
<p>The rest of the experience was a dream. I arrived at Stripe and was basically treated like a new employee. <a href="https://twitter.com/kitchenettekat">Kat Li</a> happily toured me around the office, introduced me to everyone, and made sure I had people to grab lunch with the first few days. “Python bindings? Talk with Andrew! Into Bitcoins? Check in with Christian!” Really great welcome experience.</p>
<p>Everyone treated me as a fellow Stripe. With new hires starting each week, I may as well have been.</p>
<h2 id="progress-on-urllib3">Progress on urllib3</h2>
<p>First few days were all about catching up on low-hanging fruit. Pull requests that have been thoroughly reviewed, easy little bugs or feature requests. I was closing numerous issues per day, and it felt great. Having a full day allocated to <em>just this</em> was empowering, without the distraction of attention-starved pets at home or coffeeshop patrons eyeing my prime seat next to the outlet.</p>
<p>Next up I took on a big feature that has been in the works for more than 8 months by the ever-patient <a href="http://twitter.com/kevinburke">Kevin Burke</a>: <a href="https://github.com/shazow/urllib3/pull/326">Granular retry configuration</a>. Not only did we have to design a sensible interface that we wouldn’t regret maintaining for the next 6 years, but also draw a line among many ambiguous questions like “is it safe to retry a request that has only been partially sent to the server?” For a few days, I learned what Kevin must have felt for months—hitting walls at every step, “why are half the tests suddenly failing? How do we persist this across hosts? There is no way to tell how much of the request we sent so far?”</p>
<p>Ultimately some compromises had to be made, but the feature got merged and the truck kept trucking.</p>
<p>All in all, 10 meaty pull requests were merged and 26 issues closed.</p>
<p>The big win from working full time is that it allowed me to tackle medium-large issues that otherwise I’d have to delegate to contributors. It can be weeks or months between a code review and when the contributor has a chance to iterate on the pull request. Having a contiguous work chunks allowed me to plow through what previously felt like more than six months worth of stuff.</p>
<p><img src="/posts/urllib3-stripe-and-open-source-grants/images/1.png#layoutTextWidth" alt="image"></p>
<p>urllib3 commits since moving to Github</p>
<h2 id="more-like-this">More like this</h2>
<p>Publishing and contributing to open source is going to continue happening regardless whether I’m getting paid for it or not, but it will be slow and unfocused. Which is fine, it’s how open source work has always worked. But it doesn’t need to be this way.</p>
<p>Sure, there are things <a href="https://www.gittip.com/shazow/">like Gittip</a> but the gap between an amount that would “move the needle” for me and the $1 I’m tipped per week is just too big to grapple, and I’d rather focus in short concentrated bursts than a long meandering slog.</p>
<p>If you’re a tech company, please allocate a budget for open source grants and sponsorships. Distribute it on Gittip if you wish, or do what Stripe did and fund aggressive sprints towards some high-impact milestones.</p>
<p>Consider this a formal call for sponsorship: <strong>Please help fund urllib3 development.</strong></p>
<p>If your company uses Python and benefits from urllib3 (which powers Requests, pip, and numerous other tools and libraries) and you’d like to help push the state of HTTP in Python forward, I urge you to <a href="https://urllib3.readthedocs.org/en/latest/#sponsorship">sponsor urllib3</a>.</p>
<p><a href="https://github.com/shazow/urllib3/issues?direction=desc&amp;sort=created&amp;state=open">Lots of high-value open issues</a> that could be knocked out with a few more weeks of full-time work, like <a href="https://github.com/shazow/urllib3/pull/284">adding SOCKS proxy support</a>.</p>
<p>If you’re looking for other projects to give a grant to, I would be happy to recommend some other great initiatives.</p>
<h2 id="thanks-again">Thanks again</h2>
<p>A huge cloud-scale heartfelt thanks to <a href="https://github.com/shazow/urllib3/blob/master/CONTRIBUTORS.txt">urllib3's many contributors</a> and to <a href="https://stripe.com/">Stripe</a> for helping the future come a little sooner.</p>

      ]]></content:encoded></item><item><title>Re: Facebook acquires Oculus</title><link>https://shazow.net/posts/re-facebook-acquires-oculus/</link><pubDate>Sun, 30 Mar 2014 03:37:37 +0000</pubDate><guid>https://shazow.net/posts/re-facebook-acquires-oculus/</guid><description>&lt;h2 id="a-letter-of-concern">A letter of concern&lt;/h2>
&lt;p>Hello, John Carmack.&lt;/p>
&lt;blockquote class="twitter-tweet">&lt;p lang="en" dir="ltr">(1/3) Everyone has had some time to digest the FB deal now. I think it is going to be positive, but clearly many disagree.&lt;/p>&amp;mdash; John Carmack (@ID_AA_Carmack) &lt;a href="https://twitter.com/ID_AA_Carmack/status/449594006217842688?ref_src=twsrc%5Etfw">March 28, 2014&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>


&lt;blockquote class="twitter-tweet">&lt;p lang="en" dir="ltr">(2/3) Much of the ranting has been emotional or tribal, but I am interested in reading coherent viewpoints about objective outcomes.&lt;/p>&amp;mdash; John Carmack (@ID_AA_Carmack) &lt;a href="https://twitter.com/ID_AA_Carmack/status/449594072244568064?ref_src=twsrc%5Etfw">March 28, 2014&lt;/a>&lt;/blockquote>
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>


&lt;blockquote class="twitter-tweet">&lt;p lang="en" dir="ltr">(3/3) What are the hazards? What should be done to guard against them? What are the tests for failure? Blog and I&amp;#39;ll read.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <h2 id="a-letter-of-concern">A letter of concern</h2>
<p>Hello, John Carmack.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">(1/3) Everyone has had some time to digest the FB deal now. I think it is going to be positive, but clearly many disagree.</p>&mdash; John Carmack (@ID_AA_Carmack) <a href="https://twitter.com/ID_AA_Carmack/status/449594006217842688?ref_src=twsrc%5Etfw">March 28, 2014</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<blockquote class="twitter-tweet"><p lang="en" dir="ltr">(2/3) Much of the ranting has been emotional or tribal, but I am interested in reading coherent viewpoints about objective outcomes.</p>&mdash; John Carmack (@ID_AA_Carmack) <a href="https://twitter.com/ID_AA_Carmack/status/449594072244568064?ref_src=twsrc%5Etfw">March 28, 2014</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<blockquote class="twitter-tweet"><p lang="en" dir="ltr">(3/3) What are the hazards? What should be done to guard against them? What are the tests for failure? Blog and I&#39;ll read.</p>&mdash; John Carmack (@ID_AA_Carmack) <a href="https://twitter.com/ID_AA_Carmack/status/449594122936922112?ref_src=twsrc%5Etfw">March 28, 2014</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<p>Thank you for being open to public discourse.</p>
<p>I am a passionate believer in the importance and imminence of virtual reality’s effects on our society. I was devastated when I read the news about Facebook acquiring OculusVR, and this is why:</p>
<h2 id="ecosystem">Ecosystem</h2>
<p>The Oculus Rift was more than a timely piece of clever technology—it was a powerful rallying cry to which hundreds of developers and manufacturers answered the call.</p>
<p>Within two years since Oculus Rift’s Kickstarter, the roots of a new industry took hold. Dozens of VR-related peripherals were put in the market, from haptic gloves to static treadmills to body scanners. Hundreds of demos and “VR Experiences” were published and consumed, pioneering a novel medium of entertainment and immersion. The Oculus Rift was one of the best examples of a successful “grassroots movement” that so many others try to replicate.</p>
<p>With a single announcement, much of this was undone. Numerous game developers abandoned their Oculus projects, the community roared in anger and disappointment. The trust has been broken.</p>
<p>The ecosystem has been broken.</p>
<p>We all acknowledge that Oculus VR was a private company and had the right to do what its leaders feel is necessary, but it’s clear that the expectations of the community were betrayed. The Oculus leadership was even quoted saying that they expected the core community to react negatively, so this was a calculated sacrifice.</p>
<p>I worry that this sacrifice was too big and will set our society back until the community recovers. It’s true that, with Facebook’s funds, Oculus VR could launch the consumer devices earlier, cheaper, and with better parts, but I worry it will land in a much less vibrant and healthy ecosystem than it otherwise could have.</p>
<h2 id="acquisitions">Acquisitions</h2>
<p>Even with the best intentions from both sides, acquisitions never end quite the way the participants hope they will.</p>
<p>Consider this a concern from my personal experiences, as limited and biased as they may be. I’ve heard of many concrete stories from my entrepreneurial friends of how promises get corrupted with time and bureaucracy. I can’t say I honestly believed it until it happened to me. Sure, none of these anecdotes were massive two billion dollar deals, but various degrees of autonomy and independence were definitely at play.</p>
<p>Mark Zuckerberg may feel like he is making a benevolent investment in society by advancing the roadmap of the Oculus Rift today, but things change. What if Facebook’s stock plummets next year as advertising intent continues to elude the social network? What if a competing VR firm shows success in monetizing the product in less-than-user-friendly ways? What if the SVP who is allocated to oversee your division disagrees with your vision and the slow-yet-steady political pressure erodes your founding team’s ideals? What if Instagram gets merged into Facebook Photos, WhatsApp gets merged into Facebook Messages, and you’re next on the cutting block?</p>
<h2 id="moving-forward">Moving forward</h2>
<blockquote>
<p>What are the hazards? What should be done to guard against them? What are the tests for failure?</p>
</blockquote>
<p>I don’t know.</p>
<p>While I trust the intentions of the founding team, I feel that too much of the future is in the hands of the Facebook brand and leadership.</p>
<p>If trust is lost, how will you regain it?</p>
<p>If autonomy slowly slips through your fingers, how will you notice?</p>
<p>If you’ve failed to uphold your vision, what will you do?I admit it—I initially overreacted to the acquisition news. Many of us did.</p>
<p>I hope my immediate reaction was completely wrong. I want to believe.</p>

      ]]></content:encoded></item><item><title>What is your fantasy?</title><link>https://shazow.net/posts/what-is-your-fantasy/</link><pubDate>Wed, 05 Mar 2014 01:34:09 +0000</pubDate><guid>https://shazow.net/posts/what-is-your-fantasy/</guid><description>&lt;h2 id="finding-the-north-star-for-your-career">Finding the North Star for your career&lt;/h2>
&lt;p>What do I &lt;em>really&lt;/em> want? I’ve been thinking about this for many years.&lt;/p>
&lt;p>Do I want to build a venture capital funded startup? A sustainable humble life-style business? An innovative independent game studio? A wildly-profitable consulting enterprise? Perhaps follow in the foot-steps of 37Signals, Github, Twitter, or Google?&lt;/p>
&lt;p>Or maybe I would be happy working for somebody else but I just haven’t found the right place?&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <h2 id="finding-the-north-star-for-your-career">Finding the North Star for your career</h2>
<p>What do I <em>really</em> want? I’ve been thinking about this for many years.</p>
<p>Do I want to build a venture capital funded startup? A sustainable humble life-style business? An innovative independent game studio? A wildly-profitable consulting enterprise? Perhaps follow in the foot-steps of 37Signals, Github, Twitter, or Google?</p>
<p>Or maybe I would be happy working for somebody else but I just haven’t found the right place?</p>
<p>There are no wrong answers here.</p>
<p>My fantasy is to build a company which has a very high profit-to-employee ratio sourcing from a large diversity of projects. Employees are very well compensated and operate autonomously with the benefit of the company’s retained resources.</p>
<p>The closest present manifestation of my fantasy is Valve Software due to <a href="http://www.valvesoftware.com/company/Valve_Handbook_LowRes.pdf">their very liberal project-assignment policy</a>. Just unlock the wheels on your mobile desk and move to whatever project you’re interested in working with or recruit your own team. Some projects like Half-Life 3 will linger unreleased for years to come, but this nimble freedom is the special sauce that makes every great product from Valve what it is—no matter how long it takes.</p>
<p>My fantasy is Valve, but smaller. A few dozen people, instead of a few hundred. I like to imagine us all being equal partners, remotely distributed, meeting sporadically to collaborate on larger projects, building and disbanding teams as needed.</p>
<p>It has taken me a long time to realize that this is the fantasy which appeals to me most, but even still I cannot quite articulate why.</p>
<p>I have a strong belief in the power of tiny teams with disjoint projects who work towards a common vision of the future. I believe the top percentile of performers in the tech industry are vastly under-compensated and mis-utilized while they grind away on implementing mocks handed down from a designer directed by a product manager approved by a director and budgeted by a vice president. I believe that people perform best when they’re working on the thing they’re passionate about at that moment, and that people can change.</p>
<p>My fantasy is to create a world where I can work with people who share these beliefs.</p>
<p>Now I feel like I have a goal I’m working towards.</p>
<p>Exploring our fantasy career and life is important because it allows us to introspect into what parts really matter and guide life towards some version of it. I believe this to be true even outside of the privileged bubble of tech.</p>
<p>What is your fantasy?</p>

      ]]></content:encoded></item><item><title>How to maintain a successful open source project</title><link>https://shazow.net/posts/how-to-maintain-a-successful-open-source-project/</link><pubDate>Thu, 27 Jun 2013 21:47:59 +0000</pubDate><guid>https://shazow.net/posts/how-to-maintain-a-successful-open-source-project/</guid><description>&lt;p>&lt;img src="https://shazow.net/posts/how-to-maintain-a-successful-open-source-project/images/1.png" alt="image">&lt;/p>
&lt;h3 id="lessons-learned-from-five-years-of-maintaining-one-of-the-most-used-python-libraries">Lessons learned from five years of maintaining one of the most-used Python libraries.&lt;/h3>
&lt;p>I’ve published dozens of open source projects over the years, mostly in Python and JavaScript. Few are old and stable, others are new and immature, some are wildly popular with a constant stream of contributors, but most linger until one day a trickle of users suddenly discover them. This is a guide on how to traverse these waters and foster your project into something you can be proud of.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p><img src="/posts/how-to-maintain-a-successful-open-source-project/images/1.png" alt="image"></p>
<h3 id="lessons-learned-from-five-years-of-maintaining-one-of-the-most-used-python-libraries">Lessons learned from five years of maintaining one of the most-used Python libraries.</h3>
<p>I’ve published dozens of open source projects over the years, mostly in Python and JavaScript. Few are old and stable, others are new and immature, some are wildly popular with a constant stream of contributors, but most linger until one day a trickle of users suddenly discover them. This is a guide on how to traverse these waters and foster your project into something you can be proud of.</p>
<h2 id="instructions">Instructions</h2>
<p>Every open source project needs three things: A summary of the project’s goal and approach, contribution instructions, and a license. Preferably up-front in <a href="https://github.com/shazow/urllib3/blob/master/README.rst">a README file</a>.</p>
<p>I like to include a section I call <em>Organization &amp; Philosophy.</em> This outlines how the project is structured, where everything lives, how the code is written, the kinds of tests required, and what level of performance vs simplicity to aim for. (<a href="https://github.com/shazow/unstdlib.py#organization--philosophy">See unstdlib.py for an example</a>.)</p>
<p>Then, include a <em>Contributing</em> section which outlines how to get started with the project as a developer, and all the steps necessary to get code successfully merged into the project. Here is <a href="https://github.com/shazow/urllib3#contributing">an example from urllib3</a>, which has been adopted by several other projects too:</p>
<blockquote>
<ol>
<li><a href="https://github.com/shazow/urllib3/issues">Check for open issues</a> or open a fresh issue to start a discussion around a feature idea or a bug. There is a <em>Contributor Friendly</em> tag for issues that should be ideal for people who are not very familiar with the codebase yet.</li>
<li>Fork the <a href="https://github.com/shazow/urllib3">urllib3 repository on Github</a> to start making your changes.</li>
<li>Write a test which shows that the bug was fixed or that the feature works as expected.</li>
<li>Send a pull request and bug the maintainer until it gets merged and published. :) Make sure to add yourself to CONTRIBUTORS.txt.</li>
</ol>
</blockquote>
<p>Finally, every open source project needs to have a <em>License</em>. If there is no license, then that means it’s copyrighted by the author and other people need explicit permission to use it. I default to using MIT, but you can read <a href="http://www.codinghorror.com/blog/2007/04/pick-a-license-any-license.html">Jeff Atwood’s <em>Pick a License, Any License</em></a> post for a decent summary.</p>
<h2 id="attitude">Attitude</h2>
<p>It’s incredibly important to have a good attitude towards your users and contributors.</p>
<p>Occasionally a user of your library will stumble into your issue tracker and <em>demand</em> something that is obviously silly or addressed in the documentation. What do you do?</p>
<ol>
<li>You can berate the user for rudely wasting your time.</li>
<li>You can patiently explain what the user is misunderstanding, and once they understand the problem better, invite them to send a pull request to improve the documentation for other users in their position.</li>
</ol>
<p>For some reason, many projects choose #1. Perhaps it’s because some of them are sick of their often-thankless job as a maintainer and could afford to lose some contributors. But let’s examine the effects.</p>
<p>When you berate a user, you will almost definitely lose that user, which means you also lose the possibility of that user becoming a contributor, which also means this effect could cascade to other users who witnessed the exchange, told their peers, or stumbled onto the issue years later by Googling their confusion which your documentation <em>still</em> fails to address adequately. Or worse off, you could be wrong (it does happen) and come out of it with egg dripping down your face.</p>
<p>When you’re patient, polite with a user, and encourage them to contribute to the project, they can become more invested. This also cascades to the audience. Every once in a while, <a href="https://twitter.com/Lukasaoz/status/345145330532225024">you’ll facilitate a really great relationship with someone</a> who makes a significant impact on the project and makes your life easier.</p>
<p>Maintaining the right attitude is possibly the hardest part of it—every maintainer has bad days—but civility, positivity, and progress is crucial. Not only to you and your project, but also to the community as a whole.</p>
<h2 id="marketing">Marketing</h2>
<p>You can write the most useful beautiful code in the world and be done with it, but your contribution to society can be disproportionally amplified by allowing people to reuse it <em>and</em> making people aware of it.</p>
<p><img src="/posts/how-to-maintain-a-successful-open-source-project/images/2.png#layoutTextWidth" alt="image"></p>
<p>PyPI query for “http connection pooling”</p>
<p><strong>Finding your project.</strong> Think of it like SEO. Pick a relevant name for it which ideally includes an important keyword. <em>Requests</em> is a great name for an HTTP library, and <em>urllib3</em> is less so—people often confuse it with being a standard library, but it still fares decently well when people search for things related to <em>urllib</em> and <em>urllib2</em>, which they do often due to the disappointing state of those standard libraries.</p>
<p>Your project description and first paragraph of your README should be a simple summary of your project which hits all the important keywords that people search for.</p>
<blockquote>
<p>urllib3 — Python HTTP library with thread-safe connection pooling, file post support, sanity friendly, and more.</p>
</blockquote>
<p>Python, HTTP library, thread-safe, connection pooling, file post. This is <em>exactly</em> what people search for when they run into a wall using Python’s standard HTTP libraries (<em>urllib, urllib2, httplib</em>). You can go into greater detail <a href="https://github.com/shazow/urllib3#highlights">in a subsequent <em>Highlights</em> paragraph</a>.</p>
<p>Once you’ve polished up your README, make sure to generate some documentation. Publishing it on <a href="http://readthedocs.org/">Read the Docs</a> is a great option. Include some code examples and recipes for easier adoption.</p>
<p>Finally, maintain a thorough CHANGES log and merge that into your long description. Every time a feature gets added or an important bug gets fixed, add it to your log and people will stumble on it. Also <a href="https://twitter.com/shazow/status/327495369338523648">a great place to link to when announcing a release</a>.</p>
<p><strong>Provide technical support for your audience.</strong> Set up some <a href="http://stackexchange.com/filters/new">StackOverflow alert filters</a>, and maybe even <a href="http://www.google.com/alerts">Google Alerts</a> for the odd random forum post. If you’re pushing your own software, it helps to add a disclaimer that you’re the author.</p>
<p><strong>Consider partnerships.</strong> Most code you write will not be completely standalone. Write a plugin for a framework that could use your tool and see if they’ll link to it in their README. Couple of years ago, after chatting with Kenneth Reitz on several occasions, we decided to join forces and have <em>urllib3</em> become the core of the very popular <em>Requests</em> library. At this point, <em>urllib3</em> was already decently well-known, but far behind the incumbent <em>httplib2.</em> When Kenneth and I joined forces, both of our projects flourished beyond our expectations. It was important to establish clear roles and boundaries in our partnership, and it helped that our interactions grew into a strong friendship and mutual respect. As a bonus, we have healthy cross-pollination of contributors between the two projects. Everybody won.</p>
<p><strong>Give it time.</strong> My more popular projects took at least a year to become known. This is the lazy version of marketing, but if you’ve done everything else in this article correctly, then <em>they will come.</em> Or if you’re impatient, then start hustling, attending meetups, writing blog posts, buying ads, and bribing the quasi-celebrities in your community to rave about you.</p>
<h2 id="community">Community</h2>
<p>It’s very hard to do everything alone, especially as more people come to expect things from you.</p>
<p><strong>Ask for pull requests at every opportunity.</strong> Sometimes one rogue pull request could evolve into dozens of collaborations in the future. Also, I try to ask the opinion of other members in the community whenever possible. Often they’ll know more about some RFC I never heard of, or at least have the time and initiative to research the topic further.</p>
<p><strong>Don’t be afraid to ask for help.</strong> I try to dedicate a few hours per week on open source work, but sometimes my schedule slips or I forget about a pull request. I encourage people to get involved and ping me when I need to get involved more. In more complicated branches, it helps to ask for a volunteer sub-maintainter to handle the issue until it’s ready to be considered for merging.</p>
<p><strong>Automate as much as you can.</strong> I don’t think I could have made it this far without unit tests. Last year, <a href="https://github.com/shazow/urllib3/pull/163">we instituted a 100% test coverage rule</a> for <em>urllib3</em> which made the project much easier to maintain. Any feature must be tested or else it can be removed without notice. This means every pull request must be completely tested before it is merged.</p>
<p><img src="/posts/how-to-maintain-a-successful-open-source-project/images/3.png#layoutTextWidth" alt="image"></p>
<p>Continuous Integration on Github with Travis CI</p>
<p>What’s better than unit tests? Automated unit tests! We use <a href="https://travis-ci.org/">Travis CI</a> with Github linking so that every pull request automatically gets all tests run on every platform we support. This removes me from needing to manually pull and run the tests, just to reply with “please fix the tests.” This feature is free for open source projects, and I <em>highly</em> recommend it for any project that receives regular pull requests.</p>
<p><strong>Maintain</strong> <a href="https://github.com/shazow/urllib3/blob/master/CONTRIBUTORS.txt"><strong>a list of contributors</strong></a><strong>.</strong> Remind and invite contributors to add themselves to the list, even if they just did something small. A few more bytes in the repository doesn’t cost you anything, and the pride they get from being recognized is priceless.</p>
<h2 id="have-fun">Have fun</h2>
<p>Truth be told, it has been years since I’ve done HTTP-related things which needed <em>urllib3</em>. But I continue to maintain it because I like the community and I feel a lot of pride in the project.</p>
<p>When you stop having fun, it may be a good time to hand the project off to somebody else. Find someone who truly depends on the project and cares about it more than you do. Add them as a full-privilege contributor or point to their fork as the official source. <a href="https://github.com/sstoiana/s3funnel">Sometimes it works out alright</a>. Other times the new maintainer drops out like you did, and you might need to pick up the slack again.</p>
<p>Possibly most importantly, <strong>don’t be afraid.</strong> Do your best and take pride in your work. Some people will appreciate it, other people might not. If you try to do the things in this post, then at the very least I will appreciate you for positively and effectively contributing to the Open Source community.</p>
<p><strong>Thank you for sharing your code.</strong></p>

      ]]></content:encoded></item><item><title>Laser Eye Surgery</title><link>https://shazow.net/posts/laser-eyes/</link><pubDate>Tue, 26 Feb 2013 21:24:01 -0800</pubDate><guid>https://shazow.net/posts/laser-eyes/</guid><description>&lt;p>&amp;ldquo;What was it like?&amp;rdquo; More than anything else, it was creepy.&lt;/p>
&lt;p>&lt;img src="https://github.com/shazow/everything/raw/master/laser-eyes.0.jpg" alt="On the way home">   &lt;img src="https://github.com/shazow/everything/raw/master/laser-eyes.1.jpg" alt="Happy ending">&lt;/p>
&lt;p>&amp;hellip; But worth it.&lt;/p>
&lt;h2 id="bespectacled">Bespectacled&lt;/h2>
&lt;p>My first vision correction was prescribed in the midst of my high school years.
I could see well enough to get around but with great effort and strain. By the
time I got home from school, I had to take a long nap just to make it through
the rest of the day. My eyes were exhausted and glasses fixed all that.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>&ldquo;What was it like?&rdquo; More than anything else, it was creepy.</p>
<p><img src="https://github.com/shazow/everything/raw/master/laser-eyes.0.jpg" alt="On the way home">   <img src="https://github.com/shazow/everything/raw/master/laser-eyes.1.jpg" alt="Happy ending"></p>
<p>&hellip; But worth it.</p>
<h2 id="bespectacled">Bespectacled</h2>
<p>My first vision correction was prescribed in the midst of my high school years.
I could see well enough to get around but with great effort and strain. By the
time I got home from school, I had to take a long nap just to make it through
the rest of the day. My eyes were exhausted and glasses fixed all that.</p>
<p>More than a decade later, I was ready to do almost anything to stop obsessively
wiping the lenses of my glasses. Every tiny speck or smudge was loudly
obtruding my attention, despite my my light-yet-necessary correct. Thin
contact lenses were more comfortable but didn&rsquo;t correct for my astigmatism
which left my eyes exhausted. Toric contacts were somewhat closer but the
clarity would get disturbed by each blink which shifts the orientation of the
lens.</p>
<p>While I grew to appreciate a fashionable frame of glasses, several thousand
dollars seemed like a small price to pay to eliminate a constant source of
stress in my life.</p>
<h2 id="first-day-of-my-new-life">First day of my new life</h2>
<h3 id="december-6-2012">December 6, 2012</h3>
<p>My agenda for today:</p>
<ul>
<li><em>9:45am</em> Eyedrops</li>
<li><em>1:25pm</em> Eyedrops</li>
<li><em>4:15pm</em> Eyedrops</li>
<li><em>9:00pm</em> Eyedrops</li>
<li><em>10:00pm</em> Wipe my eyelids before bed</li>
</ul>
<p>I recruit Tracy to be my ride and provider of moral support. After much
research and preparation, it&rsquo;s not hard to avoid thinking about the details of
tomorrow.</p>
<h3 id="december-7-2012">December 7, 2012</h3>
<p><em>1:15pm</em> We pull in to Dr. Furlong&rsquo;s facility in northern San Jose, CA. The
friendly staff blaze through all the paperwork I&rsquo;ve had a week to pre-read, and
we begin the signing-athon.</p>
<p>&ldquo;I understand that there is no guarantee of perfect vision&rdquo; — check. &ldquo;I
understand that this is an elective surgery and there is no health reason to
require this surgery&rdquo; — check. &ldquo;I am here for laser eye corrective surgery&rdquo; —
indeed.</p>
<p>I reveal my envelope holding a wad of $4,900 in cash that we agreed on
earlier. &ldquo;Would you like to take a mild sedative that will help you stay
relaxed during the surgery and sleep after?&rdquo; I accept the tiny sweet pill and
hold it under my tongue while it slowly dissolves.</p>
<p>The receptionist leads Tracy and me over to the <em>other</em> waiting area, where we
continue to wait for more minutes. The &ldquo;No wifi or cellphones&rdquo; sign draws
attention to the thickened walls, probably leaded just in case. One of the
technicians enters to take me in for &ldquo;prep&rdquo;.</p>
<p>I&rsquo;m seated in yet another room as the technician turns on a little boom box
dedicated to cheesy pop music. She prepares a eyedrops concoction of
anti-biotics, numbing, and lubricants. After cleaning and disinfecting my
eyes, we sit back and wait and listen to the cheerful music while the sedative
starts to take effect. My thinking begins to fog, decisions become
short-sighted, and my breathing is noticeably calmed. I make note of my
changing internal state while anticipating the next room.</p>
<p>Finally, it&rsquo;s time. We walk through the unassuming door into the operating
room. Spacious, with numerous utensils and machinery on the periphery,
surrounding the centerpiece that is <em>the chair</em>. The chair is hospital-green
leather, installed to recline and swivel between two laser machines. One
machine makes the flap followed by the other which performs the prescribed
correction.</p>
<p>Suddenly imagery of my childhood rushes in. Watching X-Files, alien
abductions, brainwashing. When I was a kid, I thought brainwashing was the
process of severing your scalp and immersing your brain in a special fluid. I
imagined it:</p>
<blockquote>
<p>&ldquo;Anesthetic is applied, followed by pressure on the tip of the head as it&rsquo;s
pulled into place with suction. You don&rsquo;t feel much but you know the top of the
head is being sliced open, scalp peeled back, revealing the brain. It&rsquo;s
floating peacefully in your head&rsquo;s natural cerebrospinal fluid until
brainwashing begins. The anonymous figures in your periphery prod with
instruments. Adjusting and correcting the physical matter of your parietal lobe
while drowning the organ over with supplemental fluids to keep it lubricated and
responsive. Results are verified and the scalp is replaced.&rdquo;</p>
</blockquote>
<p>Creepy. During my experience I was amused at how familiar it all felt to my
bizarre science fiction imagery, but instead applied to my eyes.</p>
<hr>
<p>&ldquo;Before we get started, another quick eye check and I&rsquo;m going to place some
marks on your eyes to help the tracker,&rdquo; another technician leads me to another
chin rest. As with every other eye instrument, I fit my chin and forehead into
the frame and look straight ahead. The numbing eyedrops are well in
effect by now so I felt nothing as the man gently poked my eye
with some kind of marker, once on either side of the iris.</p>
<p>All ready, off to <em>the chair</em>. I slide onto it and positioned my head to be in
range of the pivoting radius. My neck feels cradled and comfortable. More numbing
eyedrops wash over my eyes and my left eye is taped shut. I lay here
waiting for Dr. Furlong for a few minutes as he wraps up with his previous
patient. I hear subtle chatter as I stare towards the ceiling with one eye.</p>
<p>A familiar voice walks through the door and re-introduces himself. Dr. Furlong
confirm that I&rsquo;m here to get laser eye surgery and which kind-as if I would
remember the subtle differences between the various generations of laser
technology, CustomVue, Visx, WaveFront, something something something—&ldquo;I
guess,&rdquo; I respond after a pregnant pause. Smiles all around, we proceed.
&ldquo;We&rsquo;re going to clamp your eyelids open. You have a very strong blink reflex,
and while it&rsquo;s easier said than done, try to give into it the clamp as much as
you can or else your eyelids are going to be extra sore tomorrow from fighting
it.&rdquo; This is the first instance of discomfort. Once the clamps are in place,
it&rsquo;s easy to get distracted from them being there with everything else that
goes on.</p>
<p>At every step, Dr. Furlong talks through what&rsquo;s happening, what will happen
next, and what to expect. None of the discomfort was a surprise—I appreciate
this.</p>
<p>After the clamps comes the suction, but no sooner than another slew of eyedrops.
They place a tube-feeling thing around the iris which creates suction until
your vision blanks out. This was possibly the most uncomfortable part of the
process because the suction creates a lot of pressure on the eye, briefly
bordering on pain, but simultaneously distracted by the complete loss of
vision. It didn&rsquo;t feel so much as a fundamental loss of vision but more that my
vision was obstructed, like when you close your eyes. With everything in place,
I&rsquo;m swivled under the flap-creating laser and told to try to stare at the
subtle blinking orange light—the only thing I could see among my otherwise
absence of vision.</p>
<p>The laser sounds like an old DOT MATRIX printer. Tra-dat-dat-dat-dat-dat-dat.
Within just a few seconds, maybe fifteen, it&rsquo;s done. I&rsquo;m rotated slightly, the
suction is removed and my watery vision returns. I&rsquo;m sure more drops were
applied as Dr. Furlong peeled back the flap of my cornea. You&rsquo;d think this
would be the worst part, but it&rsquo;s bizarrely interesting. It takes maybe 30-45
seconds to get the flap fully open which immediately changes my vision to a
complete blur. Painless and too fascinating to delve on how creepy this is.</p>
<p>Suction is reapplied and I&rsquo;m swiveled a few inches over to
the corrective laser, again told to track the light. Ready?
Tra-dat-dat-dat-dat-dat-dat. This is when you start smelling your flesh burning
a bit—not a huge deal if you&rsquo;ve ever had a root canal, or partook
autocannibalism I suppose. With each laser burst, the peripheral of my vision
glows in an ethereal light blue, lighter each time. Not sure what
causes this effect, but it&rsquo;s like an unearthly aurora borealis inside my own
eye. Within 25 seconds, it&rsquo;s over. &ldquo;You did great!&rdquo;</p>
<p>The pressure on my eye is released and the flap carefully peeled back into the
covering position, this takes another half minute and ample eyedrops.</p>
<p>&ldquo;You can gently close your eye now,&rdquo; I comply and my freshly lasered eye is
taped shut. Time for the other eye. Unsurprisingly, process for the left eye
was essentially identical. (Fast forward a few minutes.)</p>
<hr>
<p>&ldquo;You can sit up now.&rdquo; My legs dangle off <em>the chair</em> as I&rsquo;m offered a kids
juice to help ward off dizziness, primarily from anxiety. I can already see,
well enough to walk around and get a final examination, but things are foggy
rather than blurry. Dr. Furlong admires his work as he looks at
my eyes through his microscope. &ldquo;Excellent flaps.&rdquo; We shake hands, I thank him,
and I&rsquo;m taken to the previous room where I&rsquo;m given additional drops and
care instructions. Tracy had the option to watch the surgery but opted to rejoin
me in the post-op room instead. This is where they taped the silly goggles onto
my face and gave me sunglasses to go over them. I&rsquo;m warned that there is
bruising on my eye but it will heal, that I would be extra sensitive to
light, and that there would be discomfort and scratchy pain once the numbing
eyedrops wear off. This was an understatement.</p>
<p>Tracy and I nod, pack up the supplies, and head home.</p>
<p>I&rsquo;m pretty chipper on the walk back to the car—I could basically see already,
but my mood quickly changes as the sun starts blaring through the car window
while we drive home. Like a creature of the night, I hiss and try to hide
from the light, but the scratchy pain and burning keep growing with each beam
of light revealed while Tracy navigates the car towards home. Unsure of whether
it&rsquo;s the light or just the numbing wearing off or both, I want to get home and
hide in a very dark room as soon as possible.</p>
<hr>
<p>The next hour or two suck the most, worse than the surgery itself. I take two
tylenol, which I should have done earlier. Later, I apply the numbing eyedrops
I was given, which I also should have done earlier too. Both of these things
help. I apply the numbing drops just once more after another hour, but I should have
applied them more frequently in retrospect.</p>
<p>Eventually I was able to fall asleep, and by the time three hours pass, I find
myself pain-free. My eyes still feel swollen and achey, but it&rsquo;s much more
manageable.</p>
<p>After waking up, I&rsquo;m greeted with boredom but I was instructed to keep my eyes
mostly closed for the rest of the night. So here I am, sitting in the dark,
writing the first draft of this post, with my eyes closed.</p>
<h2 id="four-months-later">Four months later</h2>
<p>&ldquo;Was it worth it?&rdquo; Absolutely.</p>
<p>Today my eye sight is almost 20/15 and I&rsquo;m down to ~3 lubricating eyedrops per
day. Halos are not an issue anymore, though I&rsquo;m still a little sensitive to
light, like sunny days or oncoming traffic at night. Nothing intolerable and
wearing sunglasses is much more enjoyable than corrective glasses. Overall, it
feels like a significant source of stress from my life has been removed.</p>
<p>The procedure itself was <em>creepy</em>, but not painful or even that scary. I admit I
was quite phobic about the whole flap thing, but after having gone through it I
feel it wasn&rsquo;t anywhere as bad as I feared. And after the first couple of
hours, the recovery was a breeze&ndash;just don&rsquo;t stinge on the numbing drops like I
did.</p>
<p>&ldquo;Should I get LASIK?&rdquo; If you mind wearing glasses or contacts, and you
can afford the pricetag, I highly recommend it. Especially if your prescription
is reasonably weak, then there is much lower risk of needing a second touchup.</p>

      ]]></content:encoded></item><item><title>List of must-have apps on OSX</title><link>https://shazow.net/posts/list-of-must-have-apps-on-osx/</link><pubDate>Fri, 30 Dec 2011 14:13:55 -0800</pubDate><guid>https://shazow.net/posts/list-of-must-have-apps-on-osx/</guid><description>&lt;p>Or, first things I install when I nest into a fresh Apple computing device:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://adium.im/">Adium&lt;/a>&lt;/li>
&lt;li>Chrome &lt;del>Firefox&lt;/del>&lt;/li>
&lt;li>&lt;a href="http://www.omh.cc/Clyppan-Clipboard-Manager-for-the-Mac/">Clyppan&lt;/a>&lt;/li>
&lt;li>Colloquy&lt;/li>
&lt;li>&lt;a href="http://dropbox.com/">Dropbox&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://growl.info/">Growl&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://mxcl.github.com/homebrew/">Homebrew&lt;/a> &lt;del>Fink&lt;/del> &lt;del>MacPorts&lt;/del>&lt;/li>
&lt;li>MacVim (via Homebrew!)&lt;/li>
&lt;li>&lt;a href="http://vibealicious.com/apps/notify/">Notifyapp&lt;/a> &lt;del>Google Notifier&lt;/del>&lt;/li>
&lt;li>Pandoraboy&lt;/li>
&lt;li>Quicksilver&lt;/li>
&lt;li>Skype&lt;/li>
&lt;li>&lt;a href="http://www.iterm2.com/">iTerm2&lt;/a> &lt;del>&lt;a href="http://totalterminal.binaryage.com/">TotalTerminal&lt;/a>&lt;/del> &lt;del>Visor&lt;/del>&lt;/li>
&lt;li>Transmission&lt;/li>
&lt;li>VLC&lt;/li>
&lt;li>Virtualbox&lt;/li>
&lt;li>XCode&lt;/li>
&lt;/ul>
&lt;p>(Based on my older post, &lt;a href="http://shazow.posterous.com/new-macbook-pro-list-of-things-i-installed-wh">New Macbook Pro, list of things I installed. What am I missing?&lt;/a>)&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>Or, first things I install when I nest into a fresh Apple computing device:</p>
<ul>
<li><a href="http://adium.im/">Adium</a></li>
<li>Chrome <del>Firefox</del></li>
<li><a href="http://www.omh.cc/Clyppan-Clipboard-Manager-for-the-Mac/">Clyppan</a></li>
<li>Colloquy</li>
<li><a href="http://dropbox.com/">Dropbox</a></li>
<li><a href="http://growl.info/">Growl</a></li>
<li><a href="http://mxcl.github.com/homebrew/">Homebrew</a> <del>Fink</del> <del>MacPorts</del></li>
<li>MacVim (via Homebrew!)</li>
<li><a href="http://vibealicious.com/apps/notify/">Notifyapp</a> <del>Google Notifier</del></li>
<li>Pandoraboy</li>
<li>Quicksilver</li>
<li>Skype</li>
<li><a href="http://www.iterm2.com/">iTerm2</a> <del><a href="http://totalterminal.binaryage.com/">TotalTerminal</a></del> <del>Visor</del></li>
<li>Transmission</li>
<li>VLC</li>
<li>Virtualbox</li>
<li>XCode</li>
</ul>
<p>(Based on my older post, <a href="http://shazow.posterous.com/new-macbook-pro-list-of-things-i-installed-wh">New Macbook Pro, list of things I installed. What am I missing?</a>)</p>

      ]]></content:encoded></item><item><title>Considering job offers? A checklist in alphabetical order</title><link>https://shazow.net/posts/checklist-for-considering-job-offers/</link><pubDate>Fri, 30 Dec 2011 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/checklist-for-considering-job-offers/</guid><description>&lt;p>&lt;img src="checklist-for-considering-job-offers.ballpit.jpg" alt="Celebrating your job offer">&lt;/p>
&lt;p>Every offer you get will include some details and omit others. To perform a
proper comparison between multiple offers and reach a good decision, you&amp;rsquo;ll
need all the details for all offers. After numerous back-and-forths with
several companies, I composed a checklist ideal for anyone on the verge of
deciding the next stage of their career.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Benefits&lt;/strong>: Medical, dental, vision. Yes, you&amp;rsquo;re young and healthy, but
root canals are much more painful when you&amp;rsquo;re paying for them out of your
pocket.&lt;/li>
&lt;li>&lt;strong>Bonuses&lt;/strong>: Sign-on bonus, moving bonus, refresher bonus, performance bonus,
yearly bonus. Some companies have them, others don&amp;rsquo;t. This affects your
&amp;ldquo;total compen sation&amp;rdquo; per year.&lt;/li>
&lt;li>&lt;strong>Career opportunities&lt;/strong>: What positions are available in the event that you
earn a promotion? What is the procedure for promotions and raises? Some
companies require a strict number of years of experience or qualification
before you can reach specific positions.&lt;/li>
&lt;li>&lt;strong>Culture&lt;/strong>: Dress code? Beer nights? Offsite trips?&lt;/li>
&lt;li>&lt;strong>Equity&lt;/strong>: Stock options and restricted stock units are two that I&amp;rsquo;ve
encountered. Find out the vesting conditions (when the stock actually becomes
yours), and more importantly find out its value. For publically traded
companies you can look up the value of each share, but for private companies
100,000 shares means nothing if you don&amp;rsquo;t know how many shares there are in
total and what class your shares are (which affects your dilution).
Ultimately, you want to find out what portion of the company value you are
getting in the event that there is a buyout. Large and VC-funded companies
will usually have less stock floating around for new hires, while smaller
privately-funded companies will usually offer more stock for a lower salary.
It&amp;rsquo;s up to you to determine how much you think that company (and thus, your
stock) is worth. I highly recommend reading &lt;a href="http://venturehacks.com/articles/job-offer">Venture Hacks&amp;rsquo; writeup on
considering offers from startups&lt;/a>
if you find yourself in that boat.&lt;/li>
&lt;li>&lt;strong>Growth support&lt;/strong>: Will they send you to relevant conferences? Book budget?
Will they pay for tuition so you can get your Master&amp;rsquo;s degree and increase
your value?&lt;/li>
&lt;li>&lt;strong>Hours&lt;/strong>: What is the company culture regarding the number of hours they&amp;rsquo;re
expecting you put in? Is it a +60hr/wk sweatshop, or do they not care how late
you stumble in and how early you bust out as long as your work is getting done?&lt;/li>
&lt;li>&lt;strong>Intellectual property&lt;/strong>: Can you continue working on your open source
projects on weekends? Does the company aggressively claim ownership on
anything you might invent outside of the office?&lt;/li>
&lt;li>&lt;strong>Leaving&lt;/strong>: You never know when your life might change and you have to jump
ship. Keep an eye out on leaving conditions and consequences, such as having
to pay back your bonuses. You don&amp;rsquo;t want to find yourself in unexpected debt.&lt;/li>
&lt;li>&lt;strong>Perks&lt;/strong>: Free lunches, gym access and trainers, public transit pass,
housing assistance, commute shuttles, relocation assistance, company events,
corporate discounts. Perks don&amp;rsquo;t pay your bills, but they don&amp;rsquo;t get taxed and
hopefully make you a little bit happier.&lt;/li>
&lt;li>&lt;strong>Team&lt;/strong>: Are you working with people you will learn from and admire or
a bunch of outsourced consultants struggling to not sabotage your company at
every step?&lt;/li>
&lt;li>&lt;strong>Salary&lt;/strong>: This is heavily proportional to the location and its cost of
living. Your salary is worth a lot less in California than it is in Seattle
where the cost of living is half, and there is no state income tax.&lt;/li>
&lt;li>&lt;strong>Starting date&lt;/strong>: Can you take a couple of months to travel Europe before
tying the corporate knot?&lt;/li>
&lt;li>&lt;strong>Telecommuting&lt;/strong>: What is the policy for working from home? What about
working abroad during an extended trip?&lt;/li>
&lt;li>&lt;strong>Title&lt;/strong>: You might not think it matters right now, but it matters to your
career. That, and salaries are often proportional to your title. Find out
what positions are available in your branch of specialty and figure out what
bracket they&amp;rsquo;re offering you. Big tech companies often have things like
Software Engineer Level 1 (new graduate), Level 2, etc.&lt;/li>
&lt;li>&lt;strong>Vacation&lt;/strong>: How many days of vacation per year? Personal days? Sick days?&lt;/li>
&lt;/ul>
&lt;h2 id="know-what-is-important-to-you-and-what-youre-worth">Know what is important to you and what you&amp;rsquo;re worth&lt;/h2>
&lt;p>Remember that everything is negotiable. If having a beer budget is super important to you and the company really wants you on the team, figure out what it would take to make your dreams come true. Worst case scenario is they&amp;rsquo;ll say &amp;ldquo;Sorry, we can&amp;rsquo;t do that because&amp;hellip;&amp;rdquo; and you can decide whether you still want the job or not.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p><img src="checklist-for-considering-job-offers.ballpit.jpg" alt="Celebrating your job offer"></p>
<p>Every offer you get will include some details and omit others. To perform a
proper comparison between multiple offers and reach a good decision, you&rsquo;ll
need all the details for all offers. After numerous back-and-forths with
several companies, I composed a checklist ideal for anyone on the verge of
deciding the next stage of their career.</p>
<ul>
<li><strong>Benefits</strong>: Medical, dental, vision. Yes, you&rsquo;re young and healthy, but
root canals are much more painful when you&rsquo;re paying for them out of your
pocket.</li>
<li><strong>Bonuses</strong>: Sign-on bonus, moving bonus, refresher bonus, performance bonus,
yearly bonus. Some companies have them, others don&rsquo;t. This affects your
&ldquo;total compen sation&rdquo; per year.</li>
<li><strong>Career opportunities</strong>: What positions are available in the event that you
earn a promotion? What is the procedure for promotions and raises? Some
companies require a strict number of years of experience or qualification
before you can reach specific positions.</li>
<li><strong>Culture</strong>: Dress code? Beer nights? Offsite trips?</li>
<li><strong>Equity</strong>: Stock options and restricted stock units are two that I&rsquo;ve
encountered. Find out the vesting conditions (when the stock actually becomes
yours), and more importantly find out its value. For publically traded
companies you can look up the value of each share, but for private companies
100,000 shares means nothing if you don&rsquo;t know how many shares there are in
total and what class your shares are (which affects your dilution).
Ultimately, you want to find out what portion of the company value you are
getting in the event that there is a buyout. Large and VC-funded companies
will usually have less stock floating around for new hires, while smaller
privately-funded companies will usually offer more stock for a lower salary.
It&rsquo;s up to you to determine how much you think that company (and thus, your
stock) is worth. I highly recommend reading <a href="http://venturehacks.com/articles/job-offer">Venture Hacks&rsquo; writeup on
considering offers from startups</a>
if you find yourself in that boat.</li>
<li><strong>Growth support</strong>: Will they send you to relevant conferences? Book budget?
Will they pay for tuition so you can get your Master&rsquo;s degree and increase
your value?</li>
<li><strong>Hours</strong>: What is the company culture regarding the number of hours they&rsquo;re
expecting you put in? Is it a +60hr/wk sweatshop, or do they not care how late
you stumble in and how early you bust out as long as your work is getting done?</li>
<li><strong>Intellectual property</strong>: Can you continue working on your open source
projects on weekends? Does the company aggressively claim ownership on
anything you might invent outside of the office?</li>
<li><strong>Leaving</strong>: You never know when your life might change and you have to jump
ship. Keep an eye out on leaving conditions and consequences, such as having
to pay back your bonuses. You don&rsquo;t want to find yourself in unexpected debt.</li>
<li><strong>Perks</strong>: Free lunches, gym access and trainers, public transit pass,
housing assistance, commute shuttles, relocation assistance, company events,
corporate discounts. Perks don&rsquo;t pay your bills, but they don&rsquo;t get taxed and
hopefully make you a little bit happier.</li>
<li><strong>Team</strong>: Are you working with people you will learn from and admire or
a bunch of outsourced consultants struggling to not sabotage your company at
every step?</li>
<li><strong>Salary</strong>: This is heavily proportional to the location and its cost of
living. Your salary is worth a lot less in California than it is in Seattle
where the cost of living is half, and there is no state income tax.</li>
<li><strong>Starting date</strong>: Can you take a couple of months to travel Europe before
tying the corporate knot?</li>
<li><strong>Telecommuting</strong>: What is the policy for working from home? What about
working abroad during an extended trip?</li>
<li><strong>Title</strong>: You might not think it matters right now, but it matters to your
career. That, and salaries are often proportional to your title. Find out
what positions are available in your branch of specialty and figure out what
bracket they&rsquo;re offering you. Big tech companies often have things like
Software Engineer Level 1 (new graduate), Level 2, etc.</li>
<li><strong>Vacation</strong>: How many days of vacation per year? Personal days? Sick days?</li>
</ul>
<h2 id="know-what-is-important-to-you-and-what-youre-worth">Know what is important to you and what you&rsquo;re worth</h2>
<p>Remember that everything is negotiable. If having a beer budget is super important to you and the company really wants you on the team, figure out what it would take to make your dreams come true. Worst case scenario is they&rsquo;ll say &ldquo;Sorry, we can&rsquo;t do that because&hellip;&rdquo; and you can decide whether you still want the job or not.</p>
<hr>
<p>Big thanks to everyone who contributed their experience to this writeup:
<a href="http://adamkwhite.com/">Adam White</a>,
<a href="http://third-bit.com/">Greg Wilson</a>,
<a href="http://www.isaacezer.com/">Isaac Ezer</a>,
Laurie, and
Vincent Shen.</p>
<p>(Based on my older post, <a href="http://blog.shazow.net/considering-offers-a-checklist-in-alphabetica">Considering offers? A checklist in alphabetical order</a>)</p>

      ]]></content:encoded></item><item><title>Password Management Utilities</title><link>https://shazow.net/posts/password-management-utilities/</link><pubDate>Mon, 20 Jun 2011 19:26:20 -0700</pubDate><guid>https://shazow.net/posts/password-management-utilities/</guid><description>&lt;p>We live in a time when cleartext usernames and passwords are leaked on a daily basis. In the last couple of months my accounts got compromised on Gawker, Sony, and MtGox. I decided it&amp;rsquo;s time to start using the one-password-per-service technique. These are tools I found in my research.&lt;/p>
&lt;p>&lt;del>I&amp;rsquo;m currently using SuperGenPass, but considering writing my own version that uses customizeable multipass sha512, more symbol breadth in passwords, and some other improvements.&lt;/del>&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>We live in a time when cleartext usernames and passwords are leaked on a daily basis. In the last couple of months my accounts got compromised on Gawker, Sony, and MtGox. I decided it&rsquo;s time to start using the one-password-per-service technique. These are tools I found in my research.</p>
<p><del>I&rsquo;m currently using SuperGenPass, but considering writing my own version that uses customizeable multipass sha512, more symbol breadth in passwords, and some other improvements.</del></p>
<p><strong>Update:</strong> I&rsquo;m using <a href="https://bitwarden.com/">Bitwarden</a> now, and you should too.</p>
<h2 id="derived-password-algorithm">Derived Password Algorithm</h2>
<h3 id="pwgenhttpsgithubcomjdodapwgen"><a href="https://github.com/jdoda/pwgen">pwgen</a></h3>
<p><a href="http://twitter.com/jondoda">@jondoda</a> wrote his own simple algorithm for deriving a per-site password based from a master password. I&rsquo;ve seen many variations of this, they&rsquo;re all very simple: Given a service name and a master password, concatenate them and run a hash function on it a bunch of times, then base64-encode the result and truncate to the desired length. Jon even wrote an Android version.</p>
<h3 id="supergenpasshttpsupergenpasscom"><a href="http://supergenpass.com/">SuperGenPass</a></h3>
<p>Same idea as Jon&rsquo;s pwgen, but in a JavaScript bookmarklet! This particular implementation uses multipass md5 which is pretty weak but the rest of the implementation is impressive. You go to the login page, enter your username and master password, then hit the bookmarklet and it converts the master password into a site-specific derived password in-place. No data leaves the comfort of your browser for this. There is also a mobile version.</p>
<h2 id="proprietary">Proprietary</h2>
<h3 id="1passwordhttpagilebitscomproducts1password"><a href="http://agilebits.com/products/1Password">1Password</a></h3>
<p>1Password is the Lexus of password management tools. It costs $39.99 for an OSX and Windows desktop license and another $11.99 for a mobile app. It lives in one file encrypted by your master password, so it&rsquo;s portable and many people use Dropbox to sync it across devices. No Linux support. (Via <a href="http://twitter.com/wolever">@wolever</a>)</p>
<h3 id="lastpass">LastPass</h3>
<p>LastPass has been sold to private equity and <a href="https://www.google.com/search?q=lastpass+leak">leaked their customer data on multiple occasions</a>. Many of their users have reported evidence that their vaults have been decrypted by third parties. Do not use LastPass.</p>
<h2 id="open-source">Open Source</h2>
<h3 id="bitwardenhttpsbitwardencom"><a href="https://bitwarden.com/">Bitwarden</a></h3>
<p>It&rsquo;s just the best, hands down. Ignore the rest, use this. It has clients on every platform, with great features and usability rivaling the best proprietary competitors, and it&rsquo;s open source with an active third-party community.</p>
<h3 id="keepasshttpkeepassinfo"><a href="http://keepass.info/">KeePass</a></h3>
<p>Official client is Windows-based but has ports to all desktop platforms (Linux, OSX, mobile). Self-contained binary and database, portable. Actively developed, has a healthy plugin community with extensions for major browsers. (Via <a href="http://twitter.com/corbett_inc">@corbett_inc</a>)</p>
<h3 id="password-gorillahttpsgithubcomzdiagorilla"><a href="https://github.com/zdia/gorilla">Password Gorilla</a></h3>
<p>Very barebones, self contained binary, actively developed. (Via <a href="http://twitter.com/seanpiled">@seanpiled</a>)</p>
<h3 id="pwsafehttppasswordsafesourceforgenet"><a href="http://passwordsafe.sourceforge.net/">pwsafe</a></h3>
<p>Even more barebones, text-based, no browser integration. (Via <a href="http://twitter.com/jpetazzo">@jpetazzo</a>)</p>
<h3 id="sflvaulthttpsprojectssavoirfairelinuxcomprojectssflvaultwiki"><a href="https://projects.savoirfairelinux.com/projects/sflvault/wiki/">SFLvault</a></h3>
<p>Somewhat unrelated but cool project, useful for sharing passwords between teams. (Via <a href="http://twitter.com/jpetazzo">@jpetazzo</a>)</p>

      ]]></content:encoded></item><item><title>Arbitrarily-Structured Data in Relational Databases</title><link>https://shazow.net/posts/arbitrarily-structured-data-in-rdbms/</link><pubDate>Wed, 20 Oct 2010 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/arbitrarily-structured-data-in-rdbms/</guid><description>&lt;p>This approach is similar to &lt;a href="http://bret.appspot.com/entry/how-friendfeed-uses-mysql">FriendFeed&amp;rsquo;s schemaless database framework&lt;/a>. The key difference is in the data locality.&lt;/p>
&lt;p>The goal is &lt;em>not&lt;/em> to build an effective schemaless database on top of a relational database, but rather to accomodate for rapidly-evolving relational schemas and reducing the difficult of migrating forward.&lt;/p>
&lt;h2 id="hypothesis">Hypothesis&lt;/h2>
&lt;p>In an evolving relational (SQL) database schema, we store two types of data: Data we will be querying against and data we will be displaying. There is often a subset of display data which will not be used for querying in the foreseeable future, and this is the data whose structure changes most often.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>This approach is similar to <a href="http://bret.appspot.com/entry/how-friendfeed-uses-mysql">FriendFeed&rsquo;s schemaless database framework</a>. The key difference is in the data locality.</p>
<p>The goal is <em>not</em> to build an effective schemaless database on top of a relational database, but rather to accomodate for rapidly-evolving relational schemas and reducing the difficult of migrating forward.</p>
<h2 id="hypothesis">Hypothesis</h2>
<p>In an evolving relational (SQL) database schema, we store two types of data: Data we will be querying against and data we will be displaying. There is often a subset of display data which will not be used for querying in the foreseeable future, and this is the data whose structure changes most often.</p>
<h2 id="solution">Solution</h2>
<p>Store query data and display data separately such that display data is less strictly-structured and thus more easily evolved.</p>
<p>Imagine a standardized table structure where each table has the following columns: <code>id</code>, <code>time_created</code>, <code>time_updated</code>, <code>_data</code>, and additional &ldquo;index columns&rdquo;.</p>
<p>The <code>_data</code> column contains a dictionary of arbitrary data serialized into JSON (or could be zlib-compressed Pickle if it were Python-specific). Index columns are columns which you query against.</p>
<h3 id="example">Example</h3>
<p>A typical <em>user</em> table might have the following columns (using an SQLAlchemy declarative model):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="n">Model</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">time_created</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">DateTime</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">time_updated</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">DateTime</span><span class="p">,</span> <span class="n">onupdate</span><span class="o">=</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">is_admin</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">Boolean</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">email</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">display_name</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">64</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">password_hash</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">40</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">password_salt</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">8</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span></code></pre></div><p>In our example, this table will have two types of queries:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="c1">-- Load the user object from the current session (where we store the user_id)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">:</span><span class="n">user_id</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">-- Check the given password against the email address, for login
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">SELECT</span><span class="w"> </span><span class="n">password_hash</span><span class="p">,</span><span class="w"> </span><span class="n">password_salt</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="k">user</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">email</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">:</span><span class="n">user_email</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>In the schemaless model, the table would look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">User</span><span class="p">(</span><span class="n">SchemalessModel</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nb">id</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">Integer</span><span class="p">,</span> <span class="n">primary_key</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">time_created</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">DateTime</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">,</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">time_updated</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">DateTime</span><span class="p">,</span> <span class="n">onupdate</span><span class="o">=</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">_data</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">JSON</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">email</span> <span class="o">=</span> <span class="n">Column</span><span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="mi">255</span><span class="p">),</span> <span class="n">nullable</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">index</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">unique</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span></code></pre></div><p>Where the <code>_data</code> column would contain data like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;display_name&#34;</span><span class="o">:</span> <span class="s2">&#34;Andrey Petrov&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;is_admin&#34;</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;password_hash&#34;</span><span class="o">:</span> <span class="s2">&#34;cSKSsy315E4EroxeDQrsxjTb6ijBxxbK&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;password_salt&#34;</span><span class="o">:</span> <span class="s2">&#34;vS5Otm&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>And perhaps we would build a framework on top of SQLAlchemy which would let us access columns as <code>user.display_name</code> or <code>user.email</code> regardless whether it&rsquo;s an extracted indexed property or a buried _data element.</p>
<h3 id="process-adding-an-index">Process: Adding an index</h3>
<ol>
<li>Build a table with just a free-structure <code>_data</code> field.</li>
<li>Determine queries, extract relevant properties into indexed columns:
<ol>
<li>ALTER TABLE to add the column</li>
<li>Run full-scan query to populate new column with data</li>
<li>Add relevant index onto said column</li>
<li>Deprecate property from <code>_data</code> (optional, we could just assume that proper columns always supercede <code>_data</code> attributes)</li>
</ol>
</li>
</ol>
<h2 id="concerns">Concerns</h2>
<ul>
<li>Adding an index may affect database performance during the process, due to the data locality. With FriendFeed&rsquo;s approach, the indices are stored in their own tables which could even be sharded across databases, so this removes any performance concerns during schema transitions. On the other hand, FriendFeed&rsquo;s approach reduces data locality which increases the number of joins required to get desired data, and also reduces the semantic meaning of the tables thus making queries more complex.</li>
<li>Performing unexpected demographic analysis on large datasets would be much slower if the fields are stored in <code>_data</code> (such as age, gender, etc) since it would require a full table scan instead of an in-database aggregate query.</li>
<li>Parsing and storing the <code>_data</code> dictionary has some performance implications, too. cPickle is best for parsing performance, perhaps zlib-compressed cPickle is best for data size and parsing performance tradeoff, but no portability beyond Python. zlib-compressed JSON reduces data size and is portable across languages, but increases parsing time. Also, this could be done natively for <a href="http://www.postgresql.org/docs/current/static/hstore.html">PostgreSQL using HSTORE</a> (<a href="http://twitter.com/#!/__jek__/status/27975347844">via <strong>jek</strong></a>)</li>
<li>Tracking mutability is important, potentially hard to do elegantly.</li>
</ul>

      ]]></content:encoded></item><item><title>Rules of thumb for novice designers (like myself)</title><link>https://shazow.net/posts/rules-of-thumb-for-novice-designers/</link><pubDate>Tue, 21 Sep 2010 15:39:53 -0400</pubDate><guid>https://shazow.net/posts/rules-of-thumb-for-novice-designers/</guid><description>&lt;p>My strategy as a novice designer is to keep track of some rules of thumb to reduce the probability of making bad mistakes from lack of experience. If I&amp;rsquo;m careful enough, then I can often get to a point where just a little polish from an experienced designer is what it takes to get it from the 80% to the 100% mark.&lt;/p>
&lt;ul>
&lt;li>Keep a &lt;strong>grid&lt;/strong>, always align to the vertical columns, but also strive to align to the horizontal rows if possible. For web design, using a CSS framework will help with this. I like &lt;a href="http://www.blueprintcss.org/">Blueprint&lt;/a> but there are many others like &lt;a href="http://960.gs/">960&lt;/a> which is more to the point.&lt;/li>
&lt;li>Pay attention to &lt;strong>rhythm&lt;/strong>: Bigger spacing interlaced with small spacing creates two rhythms instead of one. One rhythm is better than two unless there&amp;rsquo;s a good reason.&lt;/li>
&lt;li>Be &lt;strong>consistent&lt;/strong>. This includes the chrome, grid, colors, button types (link vs form button vs div), etc.&lt;/li>
&lt;li>Err on the side of more &lt;strong>spacing&lt;/strong>. Try more first, if it looks weird then try less.&lt;/li>
&lt;li>Err on the side of &lt;strong>simplicity&lt;/strong>. Question the existence of specific elements, get rid of them if they&amp;rsquo;re not absolutely necessary. You&amp;rsquo;re better off having your users not know how to do one obscure task than to be overwhelmed and not do any of the simple common tasks.&lt;/li>
&lt;li>&lt;strong>Blend colors&lt;/strong> (use &lt;a href="http://colorblendy.com/">ColorBlendy&lt;/a> or Photoshop equivalent). Colors on top of each other (think foreground/background) look more organic if they have an element of each other within them. I typically pick a background color (e.g. light blue like #abc) and a monochromatic foreground color (e.g. dark grey, #333) and multiply. This gives me the foreground color I should use with the light blue (ie. dark blue with a light blue).&lt;/li>
&lt;li>Avoid too many lines like separators, boxes, containers. Having incoherent details adds complexity and &amp;ldquo;busy-ness&amp;rdquo; to the interface. Instead, use empty space and arrangement. This is similar to adding too much punctuation to your writing.&lt;/li>
&lt;li>Focus on one primary user flow you&amp;rsquo;re optimizing for at a time. You rather the user be able to do the most important thing really easily than to be able to do everything kind of difficultly.&lt;/li>
&lt;/ul>
&lt;p>It is possible to build a beautiful interface that is completely unusable, and it&amp;rsquo;s possible to build an utilitarian and maybe unbeautiful interface which is very usable. I would aim to do the latter until you&amp;rsquo;re ready to hire a designer. It&amp;rsquo;s likely that the designer will have to rethink a lot of it from scratch as that&amp;rsquo;s just part of the design process, but that&amp;rsquo;s about as much as you can hope for.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p>My strategy as a novice designer is to keep track of some rules of thumb to reduce the probability of making bad mistakes from lack of experience. If I&rsquo;m careful enough, then I can often get to a point where just a little polish from an experienced designer is what it takes to get it from the 80% to the 100% mark.</p>
<ul>
<li>Keep a <strong>grid</strong>, always align to the vertical columns, but also strive to align to the horizontal rows if possible. For web design, using a CSS framework will help with this. I like <a href="http://www.blueprintcss.org/">Blueprint</a> but there are many others like <a href="http://960.gs/">960</a> which is more to the point.</li>
<li>Pay attention to <strong>rhythm</strong>: Bigger spacing interlaced with small spacing creates two rhythms instead of one. One rhythm is better than two unless there&rsquo;s a good reason.</li>
<li>Be <strong>consistent</strong>. This includes the chrome, grid, colors, button types (link vs form button vs div), etc.</li>
<li>Err on the side of more <strong>spacing</strong>. Try more first, if it looks weird then try less.</li>
<li>Err on the side of <strong>simplicity</strong>. Question the existence of specific elements, get rid of them if they&rsquo;re not absolutely necessary. You&rsquo;re better off having your users not know how to do one obscure task than to be overwhelmed and not do any of the simple common tasks.</li>
<li><strong>Blend colors</strong> (use <a href="http://colorblendy.com/">ColorBlendy</a> or Photoshop equivalent). Colors on top of each other (think foreground/background) look more organic if they have an element of each other within them. I typically pick a background color (e.g. light blue like #abc) and a monochromatic foreground color (e.g. dark grey, #333) and multiply. This gives me the foreground color I should use with the light blue (ie. dark blue with a light blue).</li>
<li>Avoid too many lines like separators, boxes, containers. Having incoherent details adds complexity and &ldquo;busy-ness&rdquo; to the interface. Instead, use empty space and arrangement. This is similar to adding too much punctuation to your writing.</li>
<li>Focus on one primary user flow you&rsquo;re optimizing for at a time. You rather the user be able to do the most important thing really easily than to be able to do everything kind of difficultly.</li>
</ul>
<p>It is possible to build a beautiful interface that is completely unusable, and it&rsquo;s possible to build an utilitarian and maybe unbeautiful interface which is very usable. I would aim to do the latter until you&rsquo;re ready to hire a designer. It&rsquo;s likely that the designer will have to rethink a lot of it from scratch as that&rsquo;s just part of the design process, but that&rsquo;s about as much as you can hope for.</p>
<p>These design guidelines have helped me reduce the designed-by-a-programmer effect when building consumer applications.</p>

      ]]></content:encoded></item><item><title>What it's like to stutter</title><link>https://shazow.net/posts/what-its-like-to-stutter/</link><pubDate>Sat, 06 Oct 2007 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/what-its-like-to-stutter/</guid><description>&lt;p>&lt;img src="drawing.png" alt="image">&lt;/p>
&lt;p>Drawing without using reference is one of my favourite hobbies. Struggling to pull an image out of the depths of my mind, like a fierce fish fighting for freedom. Reeling and reeling, until the moment when the fish bursts out of the still surface of the water, when the splash from the water blankets me as a sign of impending success. The rod sways unbalanced from the weight of the catch. The line could snap now, so I&amp;rsquo;m careful as pull it in and release it onto the paper. I look at the art of my creation as something new, something I haven&amp;rsquo;t seen before&amp;hellip; but the image feels familiar. I conjured this from my mind.&lt;/p>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <p><img src="drawing.png" alt="image"></p>
<p>Drawing without using reference is one of my favourite hobbies. Struggling to pull an image out of the depths of my mind, like a fierce fish fighting for freedom. Reeling and reeling, until the moment when the fish bursts out of the still surface of the water, when the splash from the water blankets me as a sign of impending success. The rod sways unbalanced from the weight of the catch. The line could snap now, so I&rsquo;m careful as pull it in and release it onto the paper. I look at the art of my creation as something new, something I haven&rsquo;t seen before&hellip; but the image feels familiar. I conjured this from my mind.</p>
<p>Getting something in your mind onto paper is a difficult process. You can&rsquo;t really focus on parts of the image in your mind. It&rsquo;s like imagining a line between two points, and then trying to focus on that line&hellip; but you can&rsquo;t, because it&rsquo;s not really there.</p>
<p>The frustration that you feel when you&rsquo;re trying to imbue your imagination onto reality, but just can&rsquo;t get it to look <em>quite right</em>. The relieving catharsis when you achieve a sense of familiarity with your creation. That is what stuttering is like.</p>
<h2 id="frequently-asked-questions">Frequently Asked Questions</h2>
<ul>
<li><strong>Are there certain things that make you stutter?</strong>
There are many factors, with a large grain of randomness. Getting too little or too much sleep makes it worse. Who I&rsquo;m talking to makes a big difference &mdash; large groups or people who I spend a lot of time with will rarely hear me stutter. If I spend a lot of time talking, I will stutter less over time. If I spend a lot of time not talking, I will stutter more. Some words are more &ldquo;high risk&rdquo; than others, in particular hard sounds like the C in <em>Computer Science</em>.</li>
<li><strong>I know what you&rsquo;re trying to say, but you can&rsquo;t get it out&hellip; should I say it for you or does that offend you?</strong>
By all means, speak up. Be my <a href="http://web.archive.org/web/20100612205138/http://en.wikipedia.org/wiki/Autocomplete">autocomplete</a>. In fact, it&rsquo;s easier for me to say a word after hearing someone else say it. Unless your guess is wrong, then it can get frustrating.</li>
<li><strong>What causes stuttering?</strong>
There is no conclusive answer. There are also various types of stuttering, most of which people grow out of after some years, and some of which you can train yourself to circumvent. There is definitely a large psychological factor, since I can speak to myself completely fluently. But there could just as easily be hard-coded physiological or neurological factors.</li>
</ul>
<p>More questions are welcome.</p>

      ]]></content:encoded></item><item><title/><link>https://shazow.net/posts/readme/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://shazow.net/posts/readme/</guid><description>&lt;h1 id="everything">Everything&lt;/h1>
&lt;h2 id="not-chronological">Not Chronological&lt;/h2>
&lt;p>This is a repository for my knowledge and thoughts about everything that is not time-sensitive.&lt;/p>
&lt;h2 id="blog-posts-i-need-to-port-to-this-repository">Blog posts I need to port to this repository&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="http://shazow.posterous.com/protip-for-ambitious-engineers-looking-for-a">Protip for ambitious engineers looking for a cool job: Forget job boards, talk to angel investors.&lt;/a>&lt;/li>
&lt;li>&lt;del>&lt;a href="https://github.com/shazow/everything/blob/master/checklist-for-considering-job-offers.md">Considering offers? A checklist in alphabetical order&lt;/a>&lt;/del>&lt;/li>
&lt;li>&lt;a href="http://shazow.net/blog/2009/02/02/circadian-somersault/">Circadian Somersault: 28 hour days for 1 week&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://shazow.net/blog/2008/10/05/passthrough-multicar-elevators/">Invention: Pass-through multi-car elevators&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://shazow.net/blog/2006/07/15/why-i-love-linux/">Why I Love Linux&lt;/a>, also &lt;a href="http://shazow.net/blog/2007/10/11/linux-love-part-2/">Part 2&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://shazow.net/blog/2007/06/10/what-its-like-to-stutter/">What it&amp;rsquo;s like to stutter&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://shazow.net/blog/2007/04/19/apartment-hunting/">Apartment Hunting: Tips, tricks, and strategy&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="things-i-should-write-about-more">Things I should write about more&lt;/h2>
&lt;ul>
&lt;li>Salary Negotiation&lt;/li>
&lt;/ul>&lt;p>...&lt;/p></description><content:encoded><![CDATA[
      <h1 id="everything">Everything</h1>
<h2 id="not-chronological">Not Chronological</h2>
<p>This is a repository for my knowledge and thoughts about everything that is not time-sensitive.</p>
<h2 id="blog-posts-i-need-to-port-to-this-repository">Blog posts I need to port to this repository</h2>
<ul>
<li><a href="http://shazow.posterous.com/protip-for-ambitious-engineers-looking-for-a">Protip for ambitious engineers looking for a cool job: Forget job boards, talk to angel investors.</a></li>
<li><del><a href="https://github.com/shazow/everything/blob/master/checklist-for-considering-job-offers.md">Considering offers? A checklist in alphabetical order</a></del></li>
<li><a href="http://shazow.net/blog/2009/02/02/circadian-somersault/">Circadian Somersault: 28 hour days for 1 week</a></li>
<li><a href="http://shazow.net/blog/2008/10/05/passthrough-multicar-elevators/">Invention: Pass-through multi-car elevators</a></li>
<li><a href="http://shazow.net/blog/2006/07/15/why-i-love-linux/">Why I Love Linux</a>, also <a href="http://shazow.net/blog/2007/10/11/linux-love-part-2/">Part 2</a></li>
<li><a href="http://shazow.net/blog/2007/06/10/what-its-like-to-stutter/">What it&rsquo;s like to stutter</a></li>
<li><a href="http://shazow.net/blog/2007/04/19/apartment-hunting/">Apartment Hunting: Tips, tricks, and strategy</a></li>
</ul>
<h2 id="things-i-should-write-about-more">Things I should write about more</h2>
<ul>
<li>Salary Negotiation</li>
</ul>

      ]]></content:encoded></item></channel></rss>