{
    "version": "https://jsonfeed.org/version/1",
    "title": "whoislewys",
    "home_page_url": "https://whoislewys.com/blog",
    "description": "whoislewys blog feed",
    "items": [
        {
            "id": "https://whoislewys.com/blog/2026/06/25/til-the-state-of-the-art-in-the-scraper-vs-bot-detection-arms-race",
            "content_html": "<p>Web scraping has always been fascinating to me – the thrill of harvesting forbidden fruit.</p><p>Today I learned just how much things have advanced.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-state-of-the-art-of-web-scraping-in-2026\">The state of the art of web scraping in 2026:<a href=\"#the-state-of-the-art-of-web-scraping-in-2026\" class=\"hash-link\" aria-label=\"Direct link to The state of the art of web scraping in 2026:\" title=\"Direct link to The state of the art of web scraping in 2026:\">​</a></h2><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"offense\">Offense<a href=\"#offense\" class=\"hash-link\" aria-label=\"Direct link to Offense\" title=\"Direct link to Offense\">​</a></h3><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"tools\">Tools<a href=\"#tools\" class=\"hash-link\" aria-label=\"Direct link to Tools\" title=\"Direct link to Tools\">​</a></h4><ul><li>Camoufox</li><li>Residential proxies</li></ul><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"secret-sauce\">Secret Sauce<a href=\"#secret-sauce\" class=\"hash-link\" aria-label=\"Direct link to Secret Sauce\" title=\"Direct link to Secret Sauce\">​</a></h4><ul><li>Proxy rotation.<ul><li>IP flagged as a bot? Try another.</li></ul></li><li>Session warming - <strong>(the craziest new concept to me that I actually saw make a difference)</strong><ul><li>Search your target website in google, bing, or duckduckgo. Browse around, look normal, smile and wave, etcetera, to build \"reputation\" for Akamai-type detectors. Once you've done this for about a minute, Akamai seems to let their guard down a bit, and you can scrape to your hearts content, as long as your bot's movements aren't too erratic.</li><li>Learned while searching through X for clues about how pro scrapers do what they do. Src: <a href=\"https://x.com/mathieulevrai1/status/2054543662021820683?s=20\" target=\"_blank\" rel=\"noopener noreferrer\">https://x.com/mathieulevrai1/status/2054543662021820683?s=20</a><ul><li>He mentioned this <a href=\"https://github.com/pim97/anti-detect-browser-tools-tech-comparison/blob/master/camoufox.md#installation--usage\" target=\"_blank\" rel=\"noopener noreferrer\">anti-detect-browser-tools-tech-comparison repo</a> which has other helpful tips and outlines the competitive landscape of other anti-bot detection tools.</li></ul></li></ul></li></ul><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"defense\">Defense<a href=\"#defense\" class=\"hash-link\" aria-label=\"Direct link to Defense\" title=\"Direct link to Defense\">​</a></h3><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"tools-the-web-scrapers-final-bosses\">Tools (the web scraper's \"final bosses\")<a href=\"#tools-the-web-scrapers-final-bosses\" class=\"hash-link\" aria-label=\"Direct link to Tools (the web scraper's &quot;final bosses&quot;)\" title=\"Direct link to Tools (the web scraper's &quot;final bosses&quot;)\">​</a></h4><ul><li>Akamai<ul><li>Behavioral tracking. Essentially, inject a script into the browser that watches your actions. Mouse movements, keystroke patterns, and browsing history are all fair game. If any of these are suss, you're flagged and either blocked or served fake data.</li></ul></li><li>Cloudflare<ul><li>Fingerprinting. Looks for fishy things like mismatch between the actual TLS fingerprint of a <code>python</code> <code>requests</code>-issued HTTP request and the fingerprint you'd expect from the request's <code>User-Agent</code>.</li></ul></li></ul><blockquote><p>For more specifics on the techniques each company uses, this <a href=\"https://gemini.google.com/app/32c194b3913f8817\" target=\"_blank\" rel=\"noopener noreferrer\">AI chat</a> is fairly comprehensive.</p></blockquote><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"tldr-what-works\">TLDR; what works?<a href=\"#tldr-what-works\" class=\"hash-link\" aria-label=\"Direct link to TLDR; what works?\" title=\"Direct link to TLDR; what works?\">​</a></h3><ul><li>Camoufox (thanks to it's advanced fingerprint injection), and<ul><li>rendering in a real display (even XVFB works and has <a href=\"https://camoufox.com/python/virtual-display/\" target=\"_blank\" rel=\"noopener noreferrer\">first-party support</a>)</li><li>a residential proxy pool,</li><li>and \"session warming\", described above,\npretty reliably bypasses Cloudflare and Akamai checks, even from a VPS. For now.\n</li></ul></li></ul><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"where-do-i-think-this-all-goes-in-the-future\">Where do I think this all goes in the future?<a href=\"#where-do-i-think-this-all-goes-in-the-future\" class=\"hash-link\" aria-label=\"Direct link to Where do I think this all goes in the future?\" title=\"Direct link to Where do I think this all goes in the future?\">​</a></h3><p>I don't see how this ends besides a GAN-type war between model apps (e.g. Firecrawl), who rely on sophisticated, dubiously legal scraping pipelines, and security providers (Akamai &amp; Cloudflare), who sell millions of enterprise contracts on protecting your data from relentless bot armies.</p><p>Unlike most other software battles, I see the odds being in favor of offense. Realistically, bot's will be able to mimic human browsing behavior perfectly, to the point here someone who really wants to protect their data from scraping needs to put the bulk of it behind a paywall or a paid API. Do you see Amazon.com doing that, dropping conversion rates, and evaporating shareholder value? No chance. But some more boutique websites definitely could. Even then, there will likely still be profit to be had at an aggregation layer which subscribes to expensive data behind paywalls, builds value on top, and captures value internally or exposes a new, higher-level platform which charges it's own spread.</p><p>For these reasons, I'm glad I upped my scraping game. It will be a valuable skill for a long time to come.</p>",
            "url": "https://whoislewys.com/blog/2026/06/25/til-the-state-of-the-art-in-the-scraper-vs-bot-detection-arms-race",
            "title": "TIL: The State of the Art in the Scraper VS. Bot-Detection Arms Race",
            "summary": "Web scraping has always been fascinating to me – the thrill of harvesting forbidden fruit.",
            "date_modified": "2026-06-25T00:00:00.000Z",
            "author": {
                "name": "Luis Gomez",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "scraping",
                "til"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/2026/03/08/ai-assisted-jupyter-notebook-development",
            "content_html": "<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-to-effectively-work-with-jupyter-notebooks-using-llms\">How to effectively work with Jupyter Notebooks using LLMS<a href=\"#how-to-effectively-work-with-jupyter-notebooks-using-llms\" class=\"hash-link\" aria-label=\"Direct link to How to effectively work with Jupyter Notebooks using LLMS\" title=\"Direct link to How to effectively work with Jupyter Notebooks using LLMS\">​</a></h2><p>I discovered some extremely effective ways to work with Jupyter Notebooks with LLM assistance this week.</p><p>Things I found massively helpful:</p><ol><li>Cell tagging</li><li>Agentic cell execution</li></ol><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"1-cell-tagging\">1. Cell Tagging<a href=\"#1-cell-tagging\" class=\"hash-link\" aria-label=\"Direct link to 1. Cell Tagging\" title=\"Direct link to 1. Cell Tagging\">​</a></h3><p>Every Jupyter Notebook is just a JSON file with a specific structure. Each cell in a Jupyter Notebook is just an entry in the <code>cells</code> list. One of the properties a cell has is <code>metadata</code>. Jupyter Notebook clients, like the built-in notebook viewer in VSCode / Cursor, have a feature which leverage this metadata property to add tags.</p><p>In VSCode, we can do this by opening the command pallete and running the \"Add Cell Tag\" command.</p><p>The JSON for a cell with source code: <code>print(\"hello world\")</code> tagged with <code>example-tag</code> looks like this:</p><div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"> </span><span class=\"token property\">\"cell_type\"</span><span class=\"token operator\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"code\"</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"> </span><span class=\"token property\">\"execution_count\"</span><span class=\"token operator\">:</span><span class=\"token plain\"> </span><span class=\"token null keyword\" style=\"color:rgb(189, 147, 249);font-style:italic\">null</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"> </span><span class=\"token property\">\"id\"</span><span class=\"token operator\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"16bb7324\"</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"> </span><span class=\"token property\">\"metadata\"</span><span class=\"token operator\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token property\">\"tags\"</span><span class=\"token operator\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">   </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"example-tag\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">}</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"> </span><span class=\"token property\">\"outputs\"</span><span class=\"token operator\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">[</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">]</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"> </span><span class=\"token property\">\"source\"</span><span class=\"token operator\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"print(\\\"hello world\\\")\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">}</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><p>Using tags allows us to reference a specific cell very easily, in a way both I and my LLM understand.</p><p>For example I can prompt my LLM with:</p><blockquote><p>In @notebooks/test.ipynb, in the cell tagged example-tag, change the print statement to all caps</p></blockquote><p>and it would very easily make the change without reading irrelevant cells and bloating context.</p><p>In this case, I could just as easily reference the cell by it's <strong>index</strong> like:</p><blockquote><p>In @notebooks/test.ipynb, In the <strong>first</strong> cell, change the print statement to all caps</p></blockquote><p>but as a notebook grows to dozens of cells, this becomes untenable.</p><p>Using cell tags scales.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"2-agentic-cell-execution\">2. Agentic Cell Execution<a href=\"#2-agentic-cell-execution\" class=\"hash-link\" aria-label=\"Direct link to 2. Agentic Cell Execution\" title=\"Direct link to 2. Agentic Cell Execution\">​</a></h3><p>Here's where my mind was really blown –&nbsp;running cells in agentic loops.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"cell-execution-with-nbconvert\">Cell execution with nbconvert<a href=\"#cell-execution-with-nbconvert\" class=\"hash-link\" aria-label=\"Direct link to Cell execution with nbconvert\" title=\"Direct link to Cell execution with nbconvert\">​</a></h4><p>Say I wanted my LLM not only to make the change to print \"hello world\" in all caps, I want it to verify that after making the change and running the cell, the output of that cell indeed prints \"HELLO WORLD\".</p><p>Again referencing the cell tag, I can ask my LLM:</p><blockquote><p>\"In @notebooks/test.ipynb in the cell tagged example-tag, change the print statement to all caps, <strong>run the cell</strong>, and verify that the printed text is in all caps\"</p></blockquote><p>In this case, your agent should use a tool maintained by the Jupyter Development Team called <a href=\"https://nbconvert.readthedocs.io/en/latest/execute_api.html#executing-notebooks-from-the-command-line\" target=\"_blank\" rel=\"noopener noreferrer\"><code>nbconvert</code></a>, which, among other things, enables Jupyter notebook execution from the command line.</p><p>For our <code>test.ipynb</code> notebook, the command will look like:</p><div class=\"language-py codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-py codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">jupyter nbconvert </span><span class=\"token operator\">-</span><span class=\"token operator\">-</span><span class=\"token plain\">to notebook </span><span class=\"token operator\">-</span><span class=\"token operator\">-</span><span class=\"token plain\">execute test</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">.</span><span class=\"token plain\">ipynb</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><p>This will populate the value of the \"outputs\" key of your cells. The agent can then look up the cell in question by its tag, read the \"outputs\" value, and verify the output using its great natural language ability or by writing an ad-hoc script.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"example-agent-session\">Example agent session<a href=\"#example-agent-session\" class=\"hash-link\" aria-label=\"Direct link to Example agent session\" title=\"Direct link to Example agent session\">​</a></h3><p>See an example session <a href=\"https://pi.dev/session/#8eaa8f0c69f1f078170ff9747629b92d\" target=\"_blank\" rel=\"noopener noreferrer\">here</a> walking through what was described above. The agent makes a code change by modifying the \"source\" value of a tagged cell, uses <code>jupyter nbconvert</code> to execute the notebook, and reads the \"outputs\" value of the cell to check that our all caps string was printed to stdout.</p>",
            "url": "https://whoislewys.com/blog/2026/03/08/ai-assisted-jupyter-notebook-development",
            "title": "LLM-assisted Jupyter Notebook Development – No MCPs needed.",
            "summary": "How to effectively work with Jupyter Notebooks using LLMS",
            "date_modified": "2026-03-08T00:00:00.000Z",
            "author": {
                "name": "Luis Gomez",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "python",
                "ai",
                "llms",
                "til"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/2025/10/09/a-better-way-to-worktree",
            "content_html": "<p>I've consistently been using worktrees for the past two years.</p><p>But I recently learned <code>git clone --bare</code> is suboptimal – there's a better way to clone a repo for worktree usage that doesn't require cluttering up your repo root or creating extra branches for the worktree workflow.</p><p>Here's the fastest, cleanest way I've found to clone a repo for usage with worktress. Works every time, without needing to run <code>git gc</code> all the time to clean up storage bloat.</p><p>I use it in zsh, but it should work in bash. (Fish users, sorry, I have no clue)</p><div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token keyword\" style=\"color:rgb(189, 147, 249);font-style:italic\">function</span><span class=\"token plain\"> </span><span class=\"token function-name function\" style=\"color:rgb(80, 250, 123)\">git-empty-clone</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">(</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(98, 114, 164)\"># Useful for cloning a repo suitable for use with git worktrees</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(98, 114, 164)\"># Taken from this stack overflow answer:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(98, 114, 164)\"># https://stackoverflow.com/questions/54367011/git-bare-repositories-worktrees-and-tracking-branches</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(189, 147, 249);font-style:italic\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">[</span><span class=\"token plain\"> </span><span class=\"token variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$#</span><span class=\"token plain\"> -lt </span><span class=\"token number\">2</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">]</span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">;</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(189, 147, 249);font-style:italic\">then</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">echo</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"Usage: git-empty-clone &lt;repo-url&gt; &lt;target-dir&gt;\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">return</span><span class=\"token plain\"> </span><span class=\"token number\">1</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(189, 147, 249);font-style:italic\">fi</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">local</span><span class=\"token plain\"> </span><span class=\"token assign-left variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">repo_url</span><span class=\"token operator\">=</span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token string variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$1</span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">local</span><span class=\"token plain\"> </span><span class=\"token assign-left variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">target_dir</span><span class=\"token operator\">=</span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token string variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$2</span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(80, 250, 123)\">git</span><span class=\"token plain\"> clone </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token string variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$repo_url</span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token string variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$target_dir</span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token plain\"> </span><span class=\"token operator\">||</span><span class=\"token plain\"> </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">return</span><span class=\"token plain\"> </span><span class=\"token number\">1</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">cd</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token string variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$target_dir</span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token plain\"> </span><span class=\"token operator\">||</span><span class=\"token plain\"> </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">return</span><span class=\"token plain\"> </span><span class=\"token number\">1</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(98, 114, 164)\"># Create an empty root commit</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">local</span><span class=\"token plain\"> empty_tree</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token assign-left variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">empty_tree</span><span class=\"token operator\">=</span><span class=\"token variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$(</span><span class=\"token variable function\" style=\"color:rgb(80, 250, 123);font-style:italic\">git</span><span class=\"token variable\" style=\"color:rgb(189, 147, 249);font-style:italic\"> hash-object -t tree /dev/null</span><span class=\"token variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">local</span><span class=\"token plain\"> empty_commit</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token assign-left variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">empty_commit</span><span class=\"token operator\">=</span><span class=\"token variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$(</span><span class=\"token variable builtin class-name\" style=\"color:rgb(189, 147, 249);font-style:italic\">echo</span><span class=\"token variable\" style=\"color:rgb(189, 147, 249);font-style:italic\"> </span><span class=\"token variable operator\" style=\"color:rgb(189, 147, 249);font-style:italic\">|</span><span class=\"token variable\" style=\"color:rgb(189, 147, 249);font-style:italic\"> </span><span class=\"token variable function\" style=\"color:rgb(80, 250, 123);font-style:italic\">git</span><span class=\"token variable\" style=\"color:rgb(189, 147, 249);font-style:italic\"> commit-tree </span><span class=\"token variable string\" style=\"color:rgb(255, 121, 198);font-style:italic\">\"</span><span class=\"token variable string variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$empty_tree</span><span class=\"token variable string\" style=\"color:rgb(255, 121, 198);font-style:italic\">\"</span><span class=\"token variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(98, 114, 164)\"># Checkout the empty commit in detached HEAD</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(80, 250, 123)\">git</span><span class=\"token plain\"> checkout </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token string variable\" style=\"color:rgb(189, 147, 249);font-style:italic\">$empty_commit</span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  </span><span class=\"token builtin class-name\" style=\"color:rgb(189, 147, 249)\">echo</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(255, 121, 198)\">\"Use git worktree add &lt;branch_name&gt; to start using worktrees.\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(248, 248, 242)\">}</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><p>Example usage to clone my vim configs repo, then add a worktree for the dev branch:</p><div class=\"codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">git-empty-clone git@github.com:whoislewys/vim_runtime.git vim_runtime.git</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">git worktree add dev</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><blockquote><p>Read <a href=\"https://docs.google.com/document/d/1FXEq3zsRa9McBUKcms64bMY7fMtGAQerzcZtPp4yISc/edit?usp=sharing\" target=\"_blank\" rel=\"noopener noreferrer\">the thread</a> to learn why it works and uncover the mystery of Git's empty tree.</p></blockquote><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"better-worktree-initialization\">Better worktree initialization<a href=\"#better-worktree-initialization\" class=\"hash-link\" aria-label=\"Direct link to Better worktree initialization\" title=\"Direct link to Better worktree initialization\">​</a></h3><p>The <code>.worktreeinclude</code> approach is perfect, and scales nicely to monorepos.</p><p>Before coming across it, I was using a post-worktree-create git hook that conflicted with husky pre-commit hooks, and I ended up having to invoke it manually so as not to bloat the pre commit hooks for team members not using worktrees. A mess.</p><blockquote><p>TODO: write support for pi, my preferred agent harness, to use <code>.worktreeinclude</code></p></blockquote>",
            "url": "https://whoislewys.com/blog/2025/10/09/a-better-way-to-worktree",
            "title": "A Better Way to Worktree",
            "summary": "I've consistently been using worktrees for the past two years.",
            "date_modified": "2025-10-09T00:00:00.000Z",
            "author": {
                "name": "Luis Gomez",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "git",
                "cli"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/2024/10/18/why-i-doxxed",
            "content_html": "<p>When I started working on crypto projects, I chose to work under a pseudonym. Here's why, and why I ultimately chose to reveal my identity.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-i-went-anon\">Why I went anon:<a href=\"#why-i-went-anon\" class=\"hash-link\" aria-label=\"Direct link to Why I went anon:\" title=\"Direct link to Why I went anon:\">​</a></h2><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"robbery-risk\">Robbery Risk<a href=\"#robbery-risk\" class=\"hash-link\" aria-label=\"Direct link to Robbery Risk\" title=\"Direct link to Robbery Risk\">​</a></h3><span style=\"width:100%;display:flex;flex-direction:column;justify-content:center;align-items:center\"><blockquote class=\"twitter-tweet\"><p lang=\"en\" dir=\"ltr\">Stay anon. <a href=\"https://t.co/IvEDWtPQhd\" target=\"_blank\" rel=\"noopener noreferrer\">https://t.co/IvEDWtPQhd</a></p>— BowTiedBull - Read Pinned Tweet or NGMI (@BowTiedBull) <a href=\"https://twitter.com/BowTiedBull/status/1655228146478641152?ref_src=twsrc%5Etfw\" target=\"_blank\" rel=\"noopener noreferrer\">May 7, 2023</a></blockquote> <script async=\"\" src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"></script><i>The less they know, the better.</i></span><br><p>Targeted robberies, SIM swaps, and harassment <strong>happen</strong>. Reducing links back to you reduce your risk.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"for-the-culture\">For the culture<a href=\"#for-the-culture\" class=\"hash-link\" aria-label=\"Direct link to For the culture\" title=\"Direct link to For the culture\">​</a></h3><p>The application developers I looked up to the most in the space were anons: TzTokChad, 0xSami, Banteg, Zeus, Fully... I could go on.</p><p>Their market knowledge and technical chops spoke for themselves. There was no need to lean on the reputation of their \"previous life\".</p><p>I wanted to be like that.</p><p>I wanted to become so good I couldn't be ignored, regardless of my name, race, or history.</p><p>Also, being anon just looked cool imo <code>\"¯\\_(ツ)_/¯\"</code></p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"career-risk\">Career Risk<a href=\"#career-risk\" class=\"hash-link\" aria-label=\"Direct link to Career Risk\" title=\"Direct link to Career Risk\">​</a></h3><p>It felt overly risky betting all of my professional reputation on such a niche field.</p><p>Using a pseudonym would allow me to build legitimacy in the space without associating my real identity with the \"crypto dev\" label - one widely associated with vaporware and scams.</p><p>Meanwhile, I could maintain my existing reputation by keeping up my traditional software skills and relationships.</p><p>In summary, I liked the optionality of pseudonymity. If crypto became more socially acceptable, I became valuable enough in the niche, or I got tired of the mask, I could always exercise my option to dox.</p><p>And that's what I chose to do.</p><hr><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-i-doxed\">Why I doxed:<a href=\"#why-i-doxed\" class=\"hash-link\" aria-label=\"Direct link to Why I doxed:\" title=\"Direct link to Why I doxed:\">​</a></h2><p>In the end, the opportunity cost of being anon bothered me. Also, I realized I was overestimating <em>my personal</em> robbery risk.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"opportunity-cost-of-accountability\">Opportunity Cost of Accountability<a href=\"#opportunity-cost-of-accountability\" class=\"hash-link\" aria-label=\"Direct link to Opportunity Cost of Accountability\" title=\"Direct link to Opportunity Cost of Accountability\">​</a></h3><blockquote><p>You have to build credibility, and you\nhave to do it under your own name as much as possible.</p><p>-<!-- --> Naval Ravikant</p></blockquote><p>While being a high-integrity anon is respectable and profitable, it can have a high opportunity cost.</p><p>If there is a large overlap between the goals of your pseudonym and your true identity, you're missing out on the compounding returns of accountability. And if you know anything about managing money, you know compounding returns are the juiciest.</p><p>Being anon is also a lot of upkeep. Keeping accounts straight across multiple SIM cards, keys, and other opsec drudgery started cutting into my personal and professional life.</p><p>That said, pseudonymity makes sense in a lot of cases.</p><p>If you can grow or have fun by expressing yourself in a way that clashes with societal norms, try it as an anon! Making a throwaway account to ask Reddit bondage questions as a Fortune 500 CEO is a wise choice. Calling yourself BasedBeffJezos and inspiring a wave of techno-optimism is also a great cause.</p><p>Or, if pseudonymity is your only hope for accomplishing a net good for humanity that the authorities would consider \"subversive\", tighten up your opsec and get to it (RIP TornadoCash).</p><p>But for my case, where my main quest is to accumulate wealth, using a pseudonym was doing more harm than good.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"overestimating-my-robbery-risk\">Overestimating my Robbery Risk<a href=\"#overestimating-my-robbery-risk\" class=\"hash-link\" aria-label=\"Direct link to Overestimating my Robbery Risk\" title=\"Direct link to Overestimating my Robbery Risk\">​</a></h3><p>Another reason I doxed is because I overestimated my personal robbery risk.</p><p>The things that put me at ease are my travel frequency and high personal security standards.</p><p>I travel frequently enough that pinning my location down remains tricky. Obviously, not everyone has this luxury. What else can you do?</p><p>Well, don't walk around with your trezor in your pocket, its code written on your forehead while live streaming your location.</p><p>On top of that, you can keep the expected returns to a violent attack on you low by making your assets difficult to retrieve. Copytrade Vitalik - store crypto assets behind a combination of hardware wallets and multisigs. Keep more traditional accounts safe with multi-factor auth methods like Yubikey and OTP codes. Don't use text messages for 2FA, or you'll get rekt by a low-tier SIM swap attack.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"fin\">Fin<a href=\"#fin\" class=\"hash-link\" aria-label=\"Direct link to Fin\" title=\"Direct link to Fin\">​</a></h2><p>May Terrance Andrew's Research Center live on in our hearts and git histories.</p><p>-<!-- --> <s>0xTARC</s> Luis</p>",
            "url": "https://whoislewys.com/blog/2024/10/18/why-i-doxxed",
            "title": "My experience working anonymously and why I doxed",
            "summary": "When I started working on crypto projects, I chose to work under a pseudonym. Here's why, and why I ultimately chose to reveal my identity.",
            "date_modified": "2024-10-18T00:00:00.000Z",
            "author": {
                "name": "Luis Gomez",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "crypto"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/2024/1/12/how-to-use-tanstack-query-usequery-with-remix-and-typescript",
            "content_html": "<p>TLDR; You can simply use: <code>useQuery&lt;SerializeFrom&lt;typeof loader&gt;&gt;</code></p><p>The blog post was meant to pile on the bandwagon of laughing at how ridiculous TypeScript types can get.</p><p>However, after playing with Shopify Oxygen, I recently learned about the (undocumented) Remix type <code>SerializeFrom</code>.</p><p>Instead of littering your code with the ridiculous thing I derive in the blog post:\n<code>useQuery&lt;Awaited&lt;ReturnType&lt;Awaited&lt;ReturnType&lt;typeof loader&gt;&gt;['json']&gt;&gt;&gt;</code></p><p>Is it bad that this common use case of wanting to re-use the data type returned from your loaders in your client is undocumented and hard to find (as of May 20 2024)? Yes of course. But at least it's there!</p><p>This also goes to show the utility of TypeScript's type system. Usually when you see a ridiculous looking type, there's a way to clean it up for downstream consumers.</p><p>Leaving the original blog post below to document my madness for posterity.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-to-use-tanstack-querys-usequery-with-remix-and-typescript\">How to use Tanstack Query's useQuery with Remix and Typescript<a href=\"#how-to-use-tanstack-querys-usequery-with-remix-and-typescript\" class=\"hash-link\" aria-label=\"Direct link to How to use Tanstack Query's useQuery with Remix and Typescript\" title=\"Direct link to How to use Tanstack Query's useQuery with Remix and Typescript\">​</a></h2><p>Using TanStack Query to fetch a resource route in your Remix app?</p><p>Want a strongly typed response?</p><p>lmaooo</p><p>JK here you go:</p><p><code>useQuery&lt;Awaited&lt;ReturnType&lt;Awaited&lt;ReturnType&lt;typeof loader&gt;&gt;['json']&gt;&gt;&gt;</code></p><p>Yay TypeScript!</p><p>Want a full example? Surely you're intrigued by now...</p><div class=\"codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">import { loader } from '~/routes/your_resource_route'</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  const {</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    data,</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    isLoading,</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    error,</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  } = useQuery&lt;Awaited&lt;ReturnType&lt;Awaited&lt;ReturnType&lt;typeof loader&gt;&gt;['json']&gt;&gt;&gt;(</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    ['your_loader_name', queryString],</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    () =&gt;</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">      fetch(`${window.location.origin}/your_resource_route`).then(</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">        (res) =&gt; res.json(),</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">      ),</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    {</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">      enabled: typeof window === 'object', // fetch only on client</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    },</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  )</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-does-that-type-work\">Why does that type work?<a href=\"#why-does-that-type-work\" class=\"hash-link\" aria-label=\"Direct link to Why does that type work?\" title=\"Direct link to Why does that type work?\">​</a></h4><p>To <code>fetch</code> and get the response in JSON format in one line, you do:</p><div class=\"codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">await(await fetch('')).json()</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><p>And loaders are pretty much just <code>fetch</code> under the hood.</p><p>So, to infer the loader's response, we use <code>Awaited&lt;ReturnType&lt;typeof loader&gt;</code></p><p>To infer the <code>.json()</code> part, we use <code>ReturnType&lt;...&gt;['json']&gt;</code></p><p>And finally, because for some reason the <code>json</code> method is async, we wrap everything in a final <code>Awaited&lt;...&gt;</code></p><div class=\"codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">&lt;Awaited&lt;ReturnType&lt;Awaited&lt;ReturnType&lt;typeof loader&gt;&gt;['json']&gt;&gt;&gt;(</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><p>Yay!</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"running-only-on-the-client\">Running only on the client<a href=\"#running-only-on-the-client\" class=\"hash-link\" aria-label=\"Direct link to Running only on the client\" title=\"Direct link to Running only on the client\">​</a></h4><p>The <code>enabled: typeof window === 'object'</code> argument sidesteps all the complex <code>initialData</code>, hydration, and dehydration stuff mentioned in the <a href=\"https://tanstack.com/query/v4/docs/react/guides/ssr#using-remix\" target=\"_blank\" rel=\"noopener noreferrer\">TanStack Query docs</a>.</p><p>Note that it will limit this fetch to the client, so this pattern should only be used for components with complex fetching logic that don't need to render right away.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-about-defer\">What about defer?<a href=\"#what-about-defer\" class=\"hash-link\" aria-label=\"Direct link to What about defer?\" title=\"Direct link to What about defer?\">​</a></h4><p>Why not use the the amazing <code>defer</code> API?</p><p>My use case is a complex chart with lots of data to paginate through. Previously I was fetching it in my loader and the page took 4 seconds to paint. Now the page paints in 0.5 seconds.</p><p>Also, the pagination makes re-using the page loader a bad idea - if I request a new chunk of data for the chart from the loader (ex.  <code>?chart_page=2</code>), the <em>entire loader</em> would run again, re-rendering the <em>entire route &amp; its children</em>. All I want is <em>the chart</em> to re-render with a new chunk of data, <a href=\"https://www.epicweb.dev/full-stack-components\" target=\"_blank\" rel=\"noopener noreferrer\">full-stack component style</a>.</p>",
            "url": "https://whoislewys.com/blog/2024/1/12/how-to-use-tanstack-query-usequery-with-remix-and-typescript",
            "title": "How to use Tanstack Query's useQuery with strong Typescript typing in Remix",
            "summary": "TLDR; You can simply use: useQuery>",
            "date_modified": "2024-01-12T00:00:00.000Z",
            "author": {
                "name": "Luis Gomez",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "Code",
                "Remix",
                "React",
                "TypeScript"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/solidity-language-server-support",
            "content_html": "<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"neovim-solc-and-cocnvim-ftw\">Neovim, solc, and coc.nvim FTW<a href=\"#neovim-solc-and-cocnvim-ftw\" class=\"hash-link\" aria-label=\"Direct link to Neovim, solc, and coc.nvim FTW\" title=\"Direct link to Neovim, solc, and coc.nvim FTW\">​</a></h2><p>Having a working knowledge of Solidity for a crypto developer in the EVM ecosystem is critical. You can be much more productive when you have the ability to dive into the source yourself. And diving into Solidity source code becomes much easier when you have deep integration in your dev environment of choice. So of course as a neck-bearded Neovim user, I wanted to share how I set this up!</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-is-a-language-server\">What is a Language Server?<a href=\"#what-is-a-language-server\" class=\"hash-link\" aria-label=\"Direct link to What is a Language Server?\" title=\"Direct link to What is a Language Server?\">​</a></h2><p>As of Solidity 0.8.11, the Solidity team has added <a href=\"https://microsoft.github.io/language-server-protocol/\" target=\"_blank\" rel=\"noopener noreferrer\">LSP</a> support to <code>solc</code>, “which means every IDE that has Language Server support will support Solidity out of the box.”</p><p><a href=\"https://blog.soliditylang.org/2021/12/20/solidity-0.8.11-release-announcement/\" target=\"_blank\" rel=\"noopener noreferrer\">https://blog.soliditylang.org/2021/12/20/solidity-0.8.11-release-announcement/</a></p><p>This makes it easy for us Vim / Neovim users to get intelligent code actions (autocomplete, go to definition, batch renames, etc.) when reading and writing Solidity!</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-to-integrate-the-solc-language-server-into-neovim-with-cocnvim\">How to Integrate the Solc language server into Neovim with coc.nvim<a href=\"#how-to-integrate-the-solc-language-server-into-neovim-with-cocnvim\" class=\"hash-link\" aria-label=\"Direct link to How to Integrate the Solc language server into Neovim with coc.nvim\" title=\"Direct link to How to Integrate the Solc language server into Neovim with coc.nvim\">​</a></h2><p>So here’s my guide on the <em>easiest, fastest</em> way to get Solidity support in your Vim / Neovim environment:</p><ol><li>Let’s install solc with a version manager. I recommend asdf. Let’s install asdf by following the instructions on their <a href=\"https://asdf-vm.com/guide/getting-started.html#_1-install-dependencies\" target=\"_blank\" rel=\"noopener noreferrer\">homepage</a>.</li></ol><blockquote><p>What is asdf? asdf is a meta version manager - it gives you a common interface for installing and switching between multiple versions of multiple languages. I recommend asdf because it makes it easy to install and switch between versions of solc, node (for Typescript!), and other languages. The only language I’ve found it doesn’t play nice with is Rust. Good thing Rustaceans have rustup!</p></blockquote><ol start=\"2\"><li><p>Now add the official Solidity plugin to asdf and use it install your desired version of <code>solc</code> by running:</p></li><li><p><code>asdf plugin add solidity [&lt;https://github.com/diegodorado/asdf-solidity.git&gt;](&lt;https://github.com/diegodorado/asdf-solidity.git&gt;)</code></p></li><li><p><code>asdf install solidity 0.8.9</code></p></li><li><p><code>asdf global solidity 0.8.9</code></p></li><li><p>You can substitute <code>0.8.9</code> for <code>latest</code>, or any other specific version of solc.</p></li><li><p>Run <code>solc --version</code> to ensure it all worked.</p></li></ol><ul><li>If you want to install a specific version of solc, say 0.8.12, you can do <code>asdf install solidity 0.8.12</code> to install, and <code>asdf global solidity 0.8.17</code> to make it available in your path</li></ul><ol start=\"3\"><li>Now install <a href=\"https://github.com/neoclide/coc.nvim\" target=\"_blank\" rel=\"noopener noreferrer\">Coc.nvim</a> by following the instructions in their README. This will allow us see LSP suggestions and run LSP actions in Vim.</li></ol><ul><li>What is Coc? The Coc.nvim plugin stands up a Node process to host VSCode extensions, allowing you to use VSCode plugins in your Vim environment! And it works in both Vim <em>and</em> Neovim - don’t be fooled by the name.</li></ul><ol start=\"4\"><li><p>Now install the coc-solidity plugin to get the Language Server actions into Vim by opening Vim / Neovim and running:</p></li><li><p><code>:CocInstall coc-solidity</code></p></li><li><p>All done! Try it out by opening a <code>.sol</code> file, going to a variable usage, and trying the “go to definition” action. If you followed the Coc.nvim <a href=\"https://github.com/neoclide/coc.nvim#example-vim-configuration\" target=\"_blank\" rel=\"noopener noreferrer\">example configuration</a>, you can do this with <code>gd</code> in normal mode.</p></li></ol><p>That was 5 easy steps to get Solidity LSP support in Vim and/or Neovim. Have fun, and please reach out on Twitter if you have questions or suggestions for future posts <a href=\"https://twitter.com/0xTARC\" target=\"_blank\" rel=\"noopener noreferrer\">@0xTARC</a>.</p><p><a href=\"#/portal/signup\">Subscribe</a></p><p>Bye for now,</p><ul><li>Tarc</li></ul><p><a href=\"#/portal/signup\">Subscribe</a></p>",
            "url": "https://whoislewys.com/blog/solidity-language-server-support",
            "title": "Solidity Support in Neovim in 5 Quick & Easy Steps! (Using Coc.nvim and asdf)",
            "summary": "Neovim, solc, and coc.nvim FTW",
            "date_modified": "2023-02-21T00:00:00.000Z",
            "author": {
                "name": "0xTARC",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "blog"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/save-your-dapp-from-getting-rekt",
            "content_html": "<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"another-day-another-package-uwu\">Another day, another package (uwu)<a href=\"#another-day-another-package-uwu\" class=\"hash-link\" aria-label=\"Direct link to Another day, another package (uwu)\" title=\"Direct link to Another day, another package (uwu)\">​</a></h2><p>I found an interesting package when I was looking into the rainbowkit source: Lavamoat.</p><p>What it do?</p><p>Quoting @metamask’s <a href=\"https://metamask.io/news/security/using-lava-moat-to-solve-software-supply-chain-security/\" target=\"_blank\" rel=\"noopener noreferrer\">Lavamoat blog</a> here,</p><blockquote><p>“JavaScript is <a href=\"https://insights.stackoverflow.com/survey/2021#technology-most-popular-technologies\" target=\"_blank\" rel=\"noopener noreferrer\">the most popular language for developers</a> by far and is also very prone to supply chain attacks. This <a href=\"https://www.sonatype.com/resources/vulnerability-timeline\" target=\"_blank\" rel=\"noopener noreferrer\">vulnerability timeline</a> from 2017 shows a solid half of the attacks originating from npm…”</p></blockquote><p>Basically, a simple <code>npm install</code> can rek you and your users in all sorts of ways! From an install script stealing your private keys (hope you use a separate wallet for development), to injecting hidden functionality into your app, supply chain attacks can hit from multiple angles.</p><p>Thankfully, @kumavis_ rode his terminal to the future and brought us back Lavamoat.</p><p>There are a few tools offered by LavaMoat in their <a href=\"https://github.com/LavaMoat/LavaMoat#how-to-secure-your-app-against-supplychain-attacks\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub repo</a> to avoid supply chain attacks at different stages, but today I’m going to show off <a href=\"https://www.npmjs.com/package/@lavamoat/allow-scripts\" target=\"_blank\" rel=\"noopener noreferrer\">@lavamoat/allow-scripts</a> since it’s an easy starting point and prevents the most common supply chain attack vector: malicious install scripts.</p><p>And, it’s easy to use.</p><p>Here’s how:</p><p>Install the package (ironic I know, but stay with me here) with:</p><p><code>yarn add -D @lavamoat/allow-scripts</code></p><p>OR</p><p><code>npm i -D @lavamoat/allow-scripts</code></p><p>Setup, which adds a <code>.yarnrc</code> or <code>.npmrc</code> and the <code>@lavamoat/preinstall-always-fail</code> package to prevent preinstall scripts from running automatically, by doing:</p><p><code>yarn allow-scripts setup</code></p><p>OR</p><p><code>npx --no-install allow-scripts setup</code></p><p>Configure your <code>package.json</code> to run necessary scripts by running:</p><p><code>yarn allow-scripts auto</code></p><p>And editing the new <code>lavamoat</code> section in your <code>package.json</code>.</p><p>You can find more details and up-to-date instructions on their <a href=\"https://www.npmjs.com/package/@lavamoat/allow-scripts\" target=\"_blank\" rel=\"noopener noreferrer\">npm page</a>!</p><p>Please subscribe to stay up-to-date with the latest and greatest in web3 frontends.</p><p><a href=\"#/portal/signup\">Subscribe now</a></p><p>And please leave a comment / DM me on Twitter letting me know what you’d like to hear about next! Tutorials on building dApps? UI/UX critiques / refactors? Open to anything.</p><p>Thanks!</p><ul><li>0xTARC</li></ul>",
            "url": "https://whoislewys.com/blog/save-your-dapp-from-getting-rekt",
            "title": "Save Your dApp from Getting Rekt By Supply Chain Attacks with Lavamoat",
            "summary": "Another day, another package (uwu)",
            "date_modified": "2023-02-04T00:00:00.000Z",
            "author": {
                "name": "0xTARC",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "blog"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/nextjs-dapps-considered-harmful-use",
            "content_html": "<p>As a web3 developer for a full year, I've read a TON of open source dApp front-end code. One thing that always concerned me was the widespread usage of Next.js.</p><p>Next.js is a server side rendering (SSR) framework for React. There's a few problems with this:</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"problems-with-nextjs-for-dapp-development\"><strong>Problems With Next.js for dApp Development</strong><a href=\"#problems-with-nextjs-for-dapp-development\" class=\"hash-link\" aria-label=\"Direct link to problems-with-nextjs-for-dapp-development\" title=\"Direct link to problems-with-nextjs-for-dapp-development\">​</a></h2><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"1-nextjs-is-too-complex-for-nearly-every-dapp\"><strong>1. Next.js is too complex for nearly every dApp.</strong><a href=\"#1-nextjs-is-too-complex-for-nearly-every-dapp\" class=\"hash-link\" aria-label=\"Direct link to 1-nextjs-is-too-complex-for-nearly-every-dapp\" title=\"Direct link to 1-nextjs-is-too-complex-for-nearly-every-dapp\">​</a></h4><p>There’s no need for the additional complexity of Next in dApps.</p><p>Do you know what Next.js consists of? It’s 38 MB, ser. I can damn near guarantee you don’t need all of that for your dApp.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"2-the-server-part-of-server-side-rendering-is-hard-to-decentralize\"><strong>2. The “server” part of server side rendering is hard to decentralize.</strong><a href=\"#2-the-server-part-of-server-side-rendering-is-hard-to-decentralize\" class=\"hash-link\" aria-label=\"Direct link to 2-the-server-part-of-server-side-rendering-is-hard-to-decentralize\" title=\"Direct link to 2-the-server-part-of-server-side-rendering-is-hard-to-decentralize\">​</a></h4><p>Decentralized server-side compute is far from a solved challenge, so most dApps just get hosted on Vercel forever.</p><p>This makes frontends built with Next the weakest link in a full-stack dApp.</p><blockquote><p>“But Taaaarc, frontends don’t really matter, users can interact with the contracts themselves, or fork the frontend and run it themselves!1!1!!</p></blockquote><ol><li>Knowing how to interact with smart contracts directly is hard. Have you tried buying an NFT off OpenSea with <code>cast</code> or <code>ethers.js</code>?</li><li>People are lazy and driven by incentives. Running a frontend is hard and expensive. No one will do it. It’s our duty to build frontends that are unstoppable by default. And open-source so <em>developers</em> can contribute to / fork them.</li></ol><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"3-next-has-several-features-that-arent-supported-by-static-builds\"><strong>3. Next has several features that aren’t supported by static builds.</strong><a href=\"#3-next-has-several-features-that-arent-supported-by-static-builds\" class=\"hash-link\" aria-label=\"Direct link to 3-next-has-several-features-that-arent-supported-by-static-builds\" title=\"Direct link to 3-next-has-several-features-that-arent-supported-by-static-builds\">​</a></h4><p>Just take a look at the <a href=\"https://nextjs.org/docs/advanced-features/static-html-export#unsupported-features\" target=\"_blank\" rel=\"noopener noreferrer\">“Unsupported Features”</a> section of the Next.js docs on static HTML exports.</p><p>A particularly damning subset:</p><ul><li>Image Optimization</li><li>Incremental Static Regeneration</li><li>getServerSideProps</li><li>Headers</li></ul><p>There are some other gotchas too that most Next users probably don’t even know about. RTFM! :3</p><p>First-class support for static builds is important because hosting a static build on a decentralized storage network like Filecoin is the best way to host a decentralized frontend.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"4-react-lock-in\"><strong>4. React lock-in.</strong><a href=\"#4-react-lock-in\" class=\"hash-link\" aria-label=\"Direct link to 4-react-lock-in\" title=\"Direct link to 4-react-lock-in\">​</a></h4><p>People who prefer Vue, for example, can feel excluded from dApp development due to the widespread usage of Next.</p><p>So anon, don’t start your next hackathon project or dApp prototype from a Next.js template.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-solution-vite\">The Solution: Vite<a href=\"#the-solution-vite\" class=\"hash-link\" aria-label=\"Direct link to The Solution: Vite\" title=\"Direct link to The Solution: Vite\">​</a></h2><blockquote><p>“Ok Next.js bad. Wat do Tarc?”</p></blockquote><p><strong>Use Vite instead.</strong></p><p>What is Vite?</p><p>It’s a blazingly-fast build tool for JavaScript. With Vite, you get:</p><ul><li>a dev server with super fast hot reloads,</li><li>hyper optimized static builds,</li><li>AND support for several different libraries so <strong>React</strong>, <strong>Vue</strong>, *and<strong>*Svelte</strong> maxis can <em>all</em> feel right at home.</li></ul><p>Did I mention Vercel has first-class support for Vite? So you can still have fast &amp; convenient preview builds through Vercel.</p><p>Sold yet? Let’s learn how to create a new React-based dApp with Vite in ONE easy command.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"using-wagmis-vite-template-to-scaffold-a-new-dapp\"><strong>Using Wagmi’s Vite Template to Scaffold a New dApp</strong><a href=\"#using-wagmis-vite-template-to-scaffold-a-new-dapp\" class=\"hash-link\" aria-label=\"Direct link to using-wagmis-vite-template-to-scaffold-a-new-dapp\" title=\"Direct link to using-wagmis-vite-template-to-scaffold-a-new-dapp\">​</a></h2><p>Let’s use the <a href=\"https://wagmi.sh/cli/create-wagmi\" target=\"_blank\" rel=\"noopener noreferrer\">create-wagmi cli</a> to scaffold a new dApp. It’s just one simple command:</p><div class=\"codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">npm init wagmi -- --template vite-react-rainbowkit</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><p>Follow the instructions from the <code>create-wagmi</code> cli to start the app. You should see something like this:\n<img loading=\"lazy\" src=\"https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F889302f3-be3b-485d-a4ff-59be5aefc274_1108x720.gif\" class=\"img_ev3q\">wagmi + rainbow &lt;3</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"fin\"><strong>Fin</strong><a href=\"#fin\" class=\"hash-link\" aria-label=\"Direct link to fin\" title=\"Direct link to fin\">​</a></h2><p>It’s important to remember that part of our jobs as software engineers is to choose the right tool for the job.</p><p>I have yet to see a case where Next.js is the right tool for building a dApp, yet it’s used EVERYWHERE in web3, even down to the dev tool documentation.</p><p>It’s time to stop. You need to be Vite-maxxing.</p><p>Thanks for reading as always and remember to subscribe for more actionable advice on Web3 frontends, UI, and UX!</p><p><a href=\"#/portal/signup\">Subscribe now</a></p><p>Bye bye</p><ul><li>0xTARC</li></ul>",
            "url": "https://whoislewys.com/blog/nextjs-dapps-considered-harmful-use",
            "title": "Next.js dApps Considered Harmful (Use THIS Instead)",
            "summary": "As a web3 developer for a full year, I've read a TON of open source dApp front-end code. One thing that always concerned me was the widespread usage of Next.js.",
            "date_modified": "2023-01-29T00:00:00.000Z",
            "author": {
                "name": "0xTARC",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "blog"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/crypto-ux-sucks-wallet-edition",
            "content_html": "<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"crypto-wallet-ux-sucks\">Crypto Wallet UX Sucks<a href=\"#crypto-wallet-ux-sucks\" class=\"hash-link\" aria-label=\"Direct link to Crypto Wallet UX Sucks\" title=\"Direct link to Crypto Wallet UX Sucks\">​</a></h2><p>It’s no mystery that interacting with blockchains is still hard compared to the buttery-slickness of today’s FinTech.</p><p>Think about the difference between buying Minecraft on the App Store with PayPal, and buying an NFT on OpenSea with MetaMask.</p><p>This is the gap we need to fill.</p><p>And the novice users aren’t the only ones struggling.</p><p>Even “crypto natives” struggle with things like</p><ul><li>sending and receiving payments</li><li>accidentally signing malicious transactions</li><li>managing multiple accounts</li><li>doing multi-sig transactions in a timely manner</li><li>needing like 27 clicks to do a simple swap</li><li>losing keys</li><li>AND MORE! WTF!</li></ul><p>We can do better 🙂</p><p>But how? First, let’s define what a wallet is. In UX-speak, what are the “affordances” of a wallet?</p><p>When I say “wallet” I’m not thinking about a hunk of metal engraved with 24 magic words. I’m thinking along the lines of MetaMask or Coinbase Wallet - something you would use every day for financial transactions. In general, this wallet should let you do the following:</p><ul><li>Generate, securely store, and backup private keys</li><li>Manage multiple accounts (e.g. a “checking account” and an “investment account”)</li><li>Share your accounts with others</li><li>Allow you to create, cancel, and see past transactions, like sending money to a friend.</li></ul><p>Most people use Trust Wallet or MetaMask for these abilities.</p><p>Some use hardware wallets like Ledgers, which can add even <em>more</em> complexity (worth it!).</p><p>And multi-sigs signers? Forget it. Advanced users only, and we quadruple check every step of the way.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"tools-and-ideas-to-make-it-better\">Tools and Ideas to Make it Better<a href=\"#tools-and-ideas-to-make-it-better\" class=\"hash-link\" aria-label=\"Direct link to Tools and Ideas to Make it Better\" title=\"Direct link to Tools and Ideas to Make it Better\">​</a></h2><p>It can be so much better. Here are some tools and ideas to improve wallet UX:</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"tools\">Tools<a href=\"#tools\" class=\"hash-link\" aria-label=\"Direct link to Tools\" title=\"Direct link to Tools\">​</a></h4><ul><li><p>Gnosis Safe Modules. <a href=\"https://github.com/gnosis/zodiac\" target=\"_blank\" rel=\"noopener noreferrer\">Zodiac</a> has a ton of great ones.</p></li><li><p>Zodiac, “The expansion pack for DAOs”, provides amazing extensions for your multi-sig wallets like Gnosis Safe. They provide <strong>Modules</strong>, like <strong><a href=\"https://github.com/gnosis/zodiac-module-exit\" target=\"_blank\" rel=\"noopener noreferrer\">Exit</a></strong> for DAOs built on Gnosis Safe to allow their members to “rage quit”, or <a href=\"https://github.com/gnosis/zodiac-module-reality\" target=\"_blank\" rel=\"noopener noreferrer\">Reality</a> to do things like execute on-chain transactions based on the results of a Snapshot vote. There’s also <strong>Modifiers</strong>, which includes <a href=\"https://github.com/gnosis/zodiac-modifier-roles\" target=\"_blank\" rel=\"noopener noreferrer\">Roles</a> for roles-based access control and <a href=\"https://github.com/gnosis/zodiac-modifier-delay\" target=\"_blank\" rel=\"noopener noreferrer\">Delay</a> for time-delayed contract execution, and <strong>Guards</strong>, like <a href=\"https://github.com/gnosis/zodiac-guard-scope\" target=\"_blank\" rel=\"noopener noreferrer\">Scope</a> for whitelisting specific contract interactions.</p></li><li><p><a href=\"https://delegate.cash/\" target=\"_blank\" rel=\"noopener noreferrer\">delegate.cash</a>, allows for safe delegation of certain transactions involving cold wallet assets from a hot wallet.</p></li><li><p><a href=\"https://metamask.io/flask/\" target=\"_blank\" rel=\"noopener noreferrer\">MetaMask Flask</a> and <a href=\"https://metamask.io/snaps/\" target=\"_blank\" rel=\"noopener noreferrer\">Snaps</a>.</p></li><li><p>MetaMask Flask is the “canary” build of MetaMask for developers that lets you install Snaps. Snaps are a really exciting feature that let you build and install extensions for MetaMask. Some of my favorite snaps:</p></li><li><p><a href=\"https://github.com/livingrockrises/snap-auto-approvals\" target=\"_blank\" rel=\"noopener noreferrer\">Snap auto approvals</a></p></li><li><p>Creates a “session” where transactions get auto-signed until the session ends. Similar to how you can sign into a bank account, do multiple transactions, then sign out. This is <em>amazing</em> for blockchain gaming.</p></li><li><p>Multi-Chain snaps, allowing you to interact with non-EVM blockchains through MetaMask.</p></li><li><p><a href=\"https://github.com/KeystoneHQ/btcsnap\" target=\"_blank\" rel=\"noopener noreferrer\">BTCSnap</a>, <a href=\"https://github.com/ChainSafe/filsnap\" target=\"_blank\" rel=\"noopener noreferrer\">FilSnap</a>, <a href=\"https://github.com/pianity/arsnap\" target=\"_blank\" rel=\"noopener noreferrer\">ArSnap</a>, <a href=\"https://github.com/ChainSafe/metamask-snap-polkadot\" target=\"_blank\" rel=\"noopener noreferrer\">metamask-snap-polkadot</a> , etc.</p></li><li><p><a href=\"https://www.coinbase.com/wallet\" target=\"_blank\" rel=\"noopener noreferrer\">Coinbase Wallet</a></p></li><li><p>The free human-readable usernames make sending and receiving money between friends a breeze. We shouldn’t need to keep a list of our friends’ public keys. An integrated fiat on-ramp too? Nice. I recommend this one, paired with a Trezor or Ledger, to people new to crypto.</p></li></ul><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"some-ideas\">Some Ideas<a href=\"#some-ideas\" class=\"hash-link\" aria-label=\"Direct link to Some Ideas\" title=\"Direct link to Some Ideas\">​</a></h4><p>Please build this!</p><ul><li>Integrate the <a href=\"https://gashawk.io/\" target=\"_blank\" rel=\"noopener noreferrer\">GasHawk</a> API into a MetaMask snap, giving you the option to schedule a transaction to be executed at a time with low gas. No extra RPC needed.</li><li>Don’t like that MetaMask now tracks your IP? Build an “AdBlock” snap (lol).</li><li>Snap that integrates with EPNS so you get alerted in MetaMask for things like a low health factor, or a friend requesting payment.</li><li>A snap for DefiLlama’s meta-dex aggregator. Always use the superior swap, anon.</li><li>This one is obvious but I can’t help mentioning it: how about a “Snap Store”? This is how Snaps are currently distributed</li></ul><p>Clunky? Well the Snap Store aggregates all Snaps, with ratings. You can even integrate payments, allowing developers to monetize MetaMask plugins.</p><ul><li>A point-of-sale / on-ramp built on the <a href=\"https://opengsn.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Gas Station Network</a>, allowing anyone with a credit card (no gas in their wallet needed) to settle transactions on crypto rails.</li><li>A Gnosis Safe Module for small investment clubs/farming as a service providers.</li><li>A Gnosis Safe Module for functionality like GasHawk’s, e.g. delaying a transaction until a time where the gas price is expected to be low.</li><li>ENS integrations like Coinbase Wallet has everywhere please.</li></ul><p>Hopefully you learned something, anon! Thanks for reading and subscribe for more actionable advice on Crypto UI, UX, and frontend development.</p><p><a href=\"#/portal/signup\">Subscribe</a></p><p>Peace :3</p><ul><li>0xTARC</li></ul><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"additional-resources\">Additional Resources:<a href=\"#additional-resources\" class=\"hash-link\" aria-label=\"Direct link to Additional Resources:\" title=\"Direct link to Additional Resources:\">​</a></h4><p>DeveloperDao’s MetaMask Snap tutorial</p>",
            "url": "https://whoislewys.com/blog/crypto-ux-sucks-wallet-edition",
            "title": "Crypto UX Sucks: Wallet Edition",
            "summary": "Crypto Wallet UX Sucks",
            "date_modified": "2023-01-21T00:00:00.000Z",
            "author": {
                "name": "0xTARC",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "blog"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/typechain-ethers-sdk-guide",
            "content_html": "<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-pain-of-calling-smart-contracts-from-the-frontend\">The Pain of Calling Smart Contracts from the Frontend<a href=\"#the-pain-of-calling-smart-contracts-from-the-frontend\" class=\"hash-link\" aria-label=\"Direct link to The Pain of Calling Smart Contracts from the Frontend\" title=\"Direct link to The Pain of Calling Smart Contracts from the Frontend\">​</a></h2><p>Have you ever</p><ul><li>Been confused about what address a contract lives at?</li><li>Used an out of date ABI?</li><li>Wondered wtf Solidity’s <code>bytes32</code> should be in TypeScript?</li><li>Got confused context switching between Solidity and TypeScript to make sure you’re calling a function correctly?</li></ul><p>Me too, anon.</p><p>What if we could have ONE place that keeps track of all of our addresses, ABIs, AND ALSO makes interacting with our contracts 10x easier? We can with TypeChain! Let’s see how in 5 simple(ish) steps.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"using-typechain-and-ethers-to-make-calling-our-contracts-10x-easier\">Using TypeChain and Ethers to Make Calling our Contracts 10x Easier<a href=\"#using-typechain-and-ethers-to-make-calling-our-contracts-10x-easier\" class=\"hash-link\" aria-label=\"Direct link to Using TypeChain and Ethers to Make Calling our Contracts 10x Easier\" title=\"Direct link to Using TypeChain and Ethers to Make Calling our Contracts 10x Easier\">​</a></h2><p>We can make our lives WAY easier in 5 simple steps:</p><ol><li>Make sure our smart contract repo has committed up-to-date ABIs</li><li>Create a New TypeScript Project for the SDK</li><li>Add Up-to-Date Addresses to the SDK</li><li>Generate TypeScript code from our ABIs using TypeChain</li><li>Use the SDK in your dApp frontend!</li></ol><p>If you’d like to follow along, the smart contract repo I’ll be using for this example is <a href=\"https://github.com/PatrickAlphaC/hardhat-smartcontract-lottery-fcc\" target=\"_blank\" rel=\"noopener noreferrer\">hardhat-smartcontract-lottery-fcc</a> from Patrick Collins’ excellent, free Full-Stack Web3 Development course.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-1make-sure-smart-contract-repository-has-up-to-date-abis\"><strong>Step 1:**</strong>Make sure Smart Contract Repository Has Up-to-Date ABIs**<a href=\"#step-1make-sure-smart-contract-repository-has-up-to-date-abis\" class=\"hash-link\" aria-label=\"Direct link to step-1make-sure-smart-contract-repository-has-up-to-date-abis\" title=\"Direct link to step-1make-sure-smart-contract-repository-has-up-to-date-abis\">​</a></h4><p>Check your smart contract repo for committed ABIs. In a default Hardhat project, it’s the <code>artifacts/</code> directory. With Foundry, the <code>out/</code> directory. Do some searching, and hopefully you’ll find those <code>.json</code> files somewhere.</p><p>If the ABIs aren’t there, compile the contracts yourself and commit the ABIs. Or ask your smart contract devs to do it (with an uwu and pwetty pwease on top).</p><p>For the <code>hardhat-smartcontract-lottery-fcc</code> repo, I did the following:</p><ol><li>Forked it</li><li>Opened <code>.gitignore</code> and removed <code>artifacts/</code></li><li>Ran <code>npx hardhat compile</code> to generate ABIs in the <code>/artifacts</code> directory</li><li>And committed all the artifacts to my fork.</li></ol><blockquote><p>It’s <em>very</em> important to have up to date ABIs committed. Ideally this process happens in CI so it’s harder for them to go missing or get out of date.</p></blockquote><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-2-create-a-new-typescript-project-for-the-sdk\"><strong>Step 2: Create a New TypeScript Project for the SDK</strong><a href=\"#step-2-create-a-new-typescript-project-for-the-sdk\" class=\"hash-link\" aria-label=\"Direct link to step-2-create-a-new-typescript-project-for-the-sdk\" title=\"Direct link to step-2-create-a-new-typescript-project-for-the-sdk\">​</a></h4><p>Now that we have our ABIs tidied up and TypeChain installed, we’re done with the smart contracts. Now we just need a basic TypeScript repo. Something like <a href=\"https://github.com/0xTARC/example-sdk/tree/12c6407\" target=\"_blank\" rel=\"noopener noreferrer\">this</a> will do the trick. I recommend using that as a reference.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-3-add-up-to-date-addresses-to-the-sdk\">Step 3: Add Up-to-Date Addresses to the SDK<a href=\"#step-3-add-up-to-date-addresses-to-the-sdk\" class=\"hash-link\" aria-label=\"Direct link to Step 3: Add Up-to-Date Addresses to the SDK\" title=\"Direct link to Step 3: Add Up-to-Date Addresses to the SDK\">​</a></h4><p>Check the docs or ask your smart contract devs where to find the addresses for the latest deployed contracts.</p><p>We’re going to put these in our SDK in a new file: <code>src/addresses.ts</code>.</p><p>Using Raffle.sol as an example:</p><div class=\"codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">export const Addresses: Record&lt;number, any&gt; = {</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  31337: {</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    Raffle: '0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9',</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  },</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  5: {</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">    Raffle: '0xBCAd87730Fa664ee16b6a599A3a5A94C020e1255',</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  },</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">}</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><p>So it maps the chain ID → Contract Name → Address.</p><p>Here I show two chains: <code>31337</code> is chain ID of the Hardhat network for local development, and <code>5</code> is Goerli, ‘cause I’m too poor for Mainnet. Hopefully it’s obvious how this can expand to support any number of EVM-ish chains.</p><blockquote><p>If we were really fancy, we’d use a continuous integration process to update the ABIs, deploy the contracts, save deployed addresses, and PR them to this repo on every push to main. But that’s a whole other post.</p></blockquote><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-4-generate-typescript-code-from-our-abis-using-typechain\">Step 4: Generate TypeScript code from our ABIs using TypeChain<a href=\"#step-4-generate-typescript-code-from-our-abis-using-typechain\" class=\"hash-link\" aria-label=\"Direct link to Step 4: Generate TypeScript code from our ABIs using TypeChain\" title=\"Direct link to Step 4: Generate TypeScript code from our ABIs using TypeChain\">​</a></h4><p>“<em>Wooow</em> Tarc, you taught us how to commit files - <em>groundbreaking</em>. And you know what ‘CI’ means… cute. What about all that ‘making smart contract interactions easy’ stuff? I’m outta here...”</p><p>Enter TypeChain.</p><p>It’s easy to use:</p><ol><li>Pull your smart contract repo into your SDK repo.</li><li>Write some scripts to generate bindings from the committed ABIs with TypeChain.</li></ol><p>Let’s go! We’re 80% there!</p><p>In your SDK repo, install your smart contract repo as a package with:</p><div class=\"codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">npm i git@github.com:0xTARC/hardhat-smartcontract-lottery-fcc.git#bb8b04d</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><p><strong>IMPORTANT</strong>: <code>#bb8b04d</code> is the latest commit hash of the smart contract repo. You want to make sure to include this when using git dependencies to avoid accidentally introducing breaking changes in your SDK.</p><p>When you’re ready for a new SDK version, you’ll re-install your smart contract repo with a new commit hash, bump the version in <code>package.json</code>, and re-release.</p><p>Now, we need to add some scripts to run <code>typechain</code> and generate code for us. Open the <code>package.json</code> of the SDK repo, go to the <code>scripts</code> section, and make it look something like this:</p><div class=\"codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#F8F8F2;--prism-background-color:#282A36\"><div class=\"codeBlockContent_biex\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">\"scripts\": {</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  \"build\": \"rm -rf ./dist &amp;&amp; tsc\",</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  \"gen-hardhat-smartcontract-lottery-bindings\": \"find ./node_modules/hardhat-smartcontract-lottery/artifacts -name '*.json' ! -name '*dbg.json' ! -path './node_modules/hardhat-smartcontract-lottery/artifacts/build-info/*' | xargs npx typechain --target ethers-v5 --out-dir src/types\",</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">  \"prebuild\": \"npm run gen-hardhat-smartcontract-lottery-bindings\"</span><br></span><span class=\"token-line\" style=\"color:#F8F8F2\"><span class=\"token plain\">},</span><br></span></code></pre><div class=\"buttonGroup__atx\"><button type=\"button\" aria-label=\"Copy code to clipboard\" title=\"Copy\" class=\"clean-btn\"><span class=\"copyButtonIcons_eSgA\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" class=\"copyButtonIcon_y97N\"><path fill=\"currentColor\" d=\"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z\"></path></svg><svg viewBox=\"0 0 24 24\" class=\"copyButtonSuccessIcon_LjdS\"><path fill=\"currentColor\" d=\"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z\"></path></svg></span></button></div></div></div><p>Finally, run <code>npm run build</code>. This will run the <code>prebuild</code> script, which runs the <code>gen-hardhat-smartcontract-lottery-bindings</code> script, which uses scary shell commands to run <code>typechain</code> on our committed ABIs from step 1.</p><p>That’s it! Your shiny new SDK is sitting in the <code>/dist</code> folder. You can use <code>npm link</code> to test locally or <code>npm publish</code> to test in prod (shoutouts Andre 🙏).</p><blockquote><p>If you have a complex system of smart contracts living in multiple repos, install those repos as NPM dependencies too, add additional <code>scripts</code> to your <code>package.json</code> (e.g. <code>\"gen-foo-bindings\": \"&lt;typechain command&gt;\"</code>) and make sure to add those new scripts them in your <code>prebuild</code> script (e.g. <code>\"npm run gen-hardhat-smartcontract-lottery-bindings &amp;&amp; npm run gen-foo-bindings”</code>).</p></blockquote><blockquote><p>You might wonder why installing a smart contract repo as an npm package is even possible, and if it would ever break. It’s admittedly a hack, and not future proof, but I it would work for 90% of cases. This is because our smart contract repo has a <code>package.json</code>, pretty much the only requirement for a Node package. This will obviously work for every Hardhat project because Hardhat is just a Node package. BUT it also works with most other projects due to the common practice of using Node packages to lint and format Solidity. For example, <a href=\"https://github.com/PaulRBerg/foundry-template\" target=\"_blank\" rel=\"noopener noreferrer\">PRB’s Foundry template</a>, and <a href=\"https://github.com/transmissions11/solmate\" target=\"_blank\" rel=\"noopener noreferrer\">Transmissions11’s solmate</a>. So If your smart contract repo <em>doesn’t</em> have a <code>package.json</code> and you’re against adding one, you can’t follow the rest of this tutorial word-for-word, but the principles are the same (use <code>curl</code> to download your repo instead, modify type generation command, etc). Anyways, let’s move on assuming you <em>can</em><code>npm install</code> your smart contract repo.</p></blockquote><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-5-using-the-sdk\">Step 5: Using the SDK<a href=\"#step-5-using-the-sdk\" class=\"hash-link\" aria-label=\"Direct link to Step 5: Using the SDK\" title=\"Direct link to Step 5: Using the SDK\">​</a></h4><p>I put together a small example repo to show how you would use this SDK’s TypeChain bindings if you were to writing some TypeScript to interact with the <code>Raffle</code> contract.</p><p>Here’s some excerpts, with some links to the full code:</p><ul><li><p>Instantiating the Contract (assuming you have a valid <code>signer</code> and/or <code>provider</code>):</p><p>  import { Addresses, Raffle<strong>factory } from '@0xtarc/example-sdk';\nconst RaffleAddress = Addresses<!-- -->[chainId]<!-- -->.Raffle;\nconst Raffle = Raffle</strong>factory.connect(RaffleAddress, signer ?? provider);</p></li><li><p>Calling <code>getEntranceFee</code> (Performing a read call):</p><p>  const entranceFee = await Raffle.getEntranceFee();</p></li></ul><p><a href=\"https://github.com/0xTARC/smartcontract-lottery-frontend-using-example-sdk/blob/0f575d185b990791dbe197efa5b3166cd90d80b7/src/App.tsx#L9-L25\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Link</a></p><ul><li><p>Calling <code>enterRaffle</code> (Performing a write call):</p><p>  const enterRaffle = () =&gt; {\nRaffle.enterRaffle({\nvalue: entranceFee,\n});\n};</p></li></ul><p><a href=\"https://github.com/0xTARC/smartcontract-lottery-frontend-using-example-sdk/blob/0f575d185b990791dbe197efa5b3166cd90d80b7/src/App.tsx#L27-L31\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Link</a></p><ul><li><p>Listening for <code>RaffleEnter</code> events and logging new entrant addresses:</p><p>  const RaffleEnter = () =&gt; {\nRaffle.on(Raffle.filters.RaffleEnter(), (entrant) =&gt; {\nconsole.log('New entrant: ', entrant);\n});\n};</p></li></ul><p><a href=\"https://github.com/0xTARC/smartcontract-lottery-frontend-using-example-sdk/blob/0f575d185b990791dbe197efa5b3166cd90d80b7/src/App.tsx#L33-L37\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Link</a></p><p>You can see the full repo <a href=\"https://github.com/0xTARC/smartcontract-lottery-frontend-using-example-sdk\" target=\"_blank\" rel=\"noopener noreferrer\">here</a>. <code>App.tsx</code> is probably the file you want. Play around and see how good the autocomplete is!</p><blockquote><p>This repo is based off the Rainbowkit example app <code>with-vite</code>, the best way to start a new dApp. That’s another post too - stay tuned.</p></blockquote><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"fin\">FIN<a href=\"#fin\" class=\"hash-link\" aria-label=\"Direct link to FIN\" title=\"Direct link to FIN\">​</a></h3><p>Hopefully you learned how to create a robust, flexible, envy-inducing SDK with TypeChain and Ethers! TypeChain is awesome and the more you work with it, the more you’ll like it.</p><p>Thanks for reading! Please subscribe to keep leveling up your Web3.0 crypto blockchain frontend skillz!</p><p><a href=\"#/portal/signup\">Subscribe now</a></p><p>Peace ✌️</p><ul><li>0xTARC</li></ul><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"more-resources\">More Resources<a href=\"#more-resources\" class=\"hash-link\" aria-label=\"Direct link to More Resources\" title=\"Direct link to More Resources\">​</a></h3><p>Patrick Collins’ FREE “Full Stack Web3 Development” course doesn’t need more shills, but I’ll shill it anyway. It’s awesome for going from Blockchain Basics → Jr. Full-Stack Web3 dev. Here’s the Smart Contract Lottery portion:</p><p><a href=\"#/portal/signup\">Subscribe</a></p>",
            "url": "https://whoislewys.com/blog/typechain-ethers-sdk-guide",
            "title": "The Ultimate Guide to Stress-Free, Multichain Smart Contract Calls in TypeScript: How to Build an SDK with TypeChain and Ethers",
            "summary": "The Pain of Calling Smart Contracts from the Frontend",
            "date_modified": "2023-01-15T00:00:00.000Z",
            "author": {
                "name": "0xTARC",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "blog"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/defi-health-factor-slider-react-component",
            "content_html": "<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-problem\">The Problem<a href=\"#the-problem\" class=\"hash-link\" aria-label=\"Direct link to The Problem\" title=\"Direct link to The Problem\">​</a></h2><p>So you need your users to choose from a range of values while giving them a hint about how risky their choice is.</p><p>Maybe you reach for Material UI, install 6MB of potentially dangerous dependencies for a single, hard to customize component.</p><p>Maybe you're bought-in to the headless component wave and are in the middle of migrating to your own design system (my case!).</p><p>Or maybe you just want a great slider with minimal dependencies.</p><p>No matter your situation, this one's for you, anon.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-code\">The Code<a href=\"#the-code\" class=\"hash-link\" aria-label=\"Direct link to The Code\" title=\"Direct link to The Code\">​</a></h2><p>So, here's the Slider. It’s based on a Slider I built for the canonical FIAT protocol frontend.</p><p>It’s built with Typescript, React, and Radix UI. The Slider is styled with Stitches, though with a little effort you can swap for CSS Modules or another styling solution.\n<img loading=\"lazy\" src=\"https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6609b3f8-b240-4a44-926e-8fa1210c2fee_1618x938.gif\" class=\"img_ev3q\">\n<a href=\"https://codesandbox.io/s/github/0xTARC/HealthFactorSlider/tree/main/\" target=\"_blank\" rel=\"noopener noreferrer\">Edit on CodeSandbox</a></p><p>Details of note:</p><ul><li>The Slider’s track is white by default. The gradient is implemented as a <code>color</code> variant with Stitches. Variants are a powerful way to express a lot of style without a lot of code. Plus, a ton of devs are used to using variants thanks to MUI (<code>color='primary’</code>, anybody?). If you're interested, read more on variants <a href=\"https://ped.ro/writing/variant-driven-components\" target=\"_blank\" rel=\"noopener noreferrer\">here</a>.</li><li>A <a href=\"https://github.com/0xTARC/HealthFactorSlider/blob/main/src/Slider/Slider.tsx#L124\" target=\"_blank\" rel=\"noopener noreferrer\">custom data attribute</a>, <code>[data-inverted]</code>, is added to SliderRoot. This is then used as a <a href=\"https://github.com/0xTARC/HealthFactorSlider/blob/main/src/Slider/Slider.tsx#L41\" target=\"_blank\" rel=\"noopener noreferrer\">selector</a> to reverse the gradient. This is in line with the intuitive Radix component APIs, and makes it easy to port the styling of this component over to CSS Modules, or another styling solution. (CSS Modules ftw, I just happened to have Stitches installed because we're migrating off Next UI.)</li><li>Fully typed so devs will know all valid color values without having to read the source.</li></ul><p><a href=\"https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d4231d-7dd7-4b82-80d2-a9dfbe312d8f_1012x760.gif\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" src=\"https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4d4231d-7dd7-4b82-80d2-a9dfbe312d8f_1012x760.gif\" alt=\"Slider color prop autocompletion\" class=\"img_ev3q\"></a>Good types = good autocompletion = happy devs\nHad to do some Typescript-fu to get that to work, just look at how wonky the <a href=\"https://github.com/0xTARC/HealthFactorSlider/blob/main/src/Slider/Slider.tsx#L75-L89\" target=\"_blank\" rel=\"noopener noreferrer\">SliderProps interface</a> is. But from my experience with design systems, wonky types are often the case when creating components that will have good autocompletion for devs.</p><p>Tried for a few mins to replicate that <em>choice</em> Developer eXperience™️ with <code>vanilla-extract</code> but couldn't quite get it. If you can, please message me and I'll add that example to the CodeSandbox &amp; repo.</p><p>Thanks for reading! :3</p><ul><li>0xTARC</li></ul><p><a href=\"#/portal/signup\">Subscribe now</a></p>",
            "url": "https://whoislewys.com/blog/defi-health-factor-slider-react-component",
            "title": "Steal my Code & Save Time: A Step-by-Step Guide to Building a Buttery Smooth, Composable, Health Factor Slider",
            "summary": "The Problem",
            "date_modified": "2023-01-07T00:00:00.000Z",
            "author": {
                "name": "0xTARC",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "blog"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/2019/09/06/Notes-From-The-Mystery-Machine_Bus",
            "content_html": "<p>What follows is an exact repost/mirror of Steve Yegge's 2012 post \"Notes from the Mystery Machine Bus\" from Google+ on the software risk aversion axis. It's not even accessible from the WaybackMachine. Enjoy.</p><h1>Notes from the Mystery Machine Bus</h1><p>I've spent the past eight years (starting back in June 2004) writing elaborate rants about a bunch of vaguely related software engineering issues.</p><p>I was doing all that ranting because I've been genuinely perplexed by a set of \"bizarre\" world-views held dear by -- as far as I can tell -- about half of all programmers I encounter, whether online or in person.</p><p>Last week, after nearly a decade of hurling myself against this problem, I've finally figured it out. I know exactly what's been bothering me.</p><p>In today's essay I'm going to present you with a new conceptual framework for thinking about software engineering. This set of ideas I present will be completely obvious to you. You will probably slap yourself for not having thought of it yourself. Or you might slap the person next to you.  In fact you probably have thought of it yourself, because it is so blindingly obvious.</p><p>But in my thirty-odd years as a programmer I'm pretty sure this way of thinking about things, if it already existed, has never been mainstream. That assertion is backed by what has to be at least ten Google searches that turned up nothing. So I'm pretty confident.</p><p>I'm going to make it mainstream, right now. Watch!</p><p>And I suspect this conceptual framework I'm giving you will immediately become, and forever remain, one of the most important tools in your toolkit for talking with -- and about -- other programmers.</p><p>The punch line, a.k.a. TL;DR</p><p>I won't keep you in suspense. Here is the thesis of this looooong essay. It is the root cause that motivated over half of my ranting all these years, starting at Amazon and continuing here at Google.</p><p>(Note: I Do Not Speak For My Employer. This should be patently obvious. When employers want someone to speak for them, they hire a person like the Mouth of Sauron, to make absolutely sure everyone knows they are speaking for the Employer.)</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"my-thesis\">My thesis:<a href=\"#my-thesis\" class=\"hash-link\" aria-label=\"Direct link to My thesis:\" title=\"Direct link to My thesis:\">​</a></h2><ol><li>Software engineering has its own political axis, ranging from conservative to liberal.</li></ol><p>(Note: Technically, you could stop reading right here and be at pretty much 90% comprehension. In case you care.)</p><ol start=\"2\"><li><p>The notions of \"conservative\" and \"liberal\" on this political axis are specialized to software engineering. But they exhibit some strong similarities to their counterparts in real-world politics.</p></li><li><p>Everyone in the software industry who does stuff related to programming computers falls somewhere fairly precise on this political spectrum, whether they realize it or not.</p></li></ol><p>Put another way, YOU are either a liberal or a conservative software engineer. You may be more of a centrist, or maybe an extremist, but you fall somewhere on that left/right spectrum.</p><p>Just as in real-world politics, software conservatism and liberalism are radically different world views. Make no mistake: they are at odds. They have opposing value systems, priorities, core beliefs and motivations. These value systems clash at design time, at implementation time, at diagnostic time, at recovery time. They get along like green eggs and ham.</p><p>I think it is important for us to recognize and understand the conservative/liberal distinction in our industry. It probably won't help us agree on anything, pretty much by definition. Any particular issue only makes it onto the political axis if there is a fundamental, irreconcilable difference of opinion about it. Programmers probably won't -- or maybe even can't -- change their core value systems.</p><p>But the political-axis framework gives us a familiar set of ideas and terms for identifying areas of fundamental disagreement. This can lead to faster problem resolution. Being able to identify something quickly as a well-defined political issue means we can stop wasting time trying to convince the other side to change their minds, and instead move directly into the resolution phase, which (just as in politics) generally boils down to negotiation and compromise. Or, you know, Watergate.</p><p>Are real politics and software politics correlated?</p><p>Does being a political conservative make you more likely to be a software conservative? I think all we have are hunches and intuition at this point. We'd need studies for an answer with any scientific basis.</p><p>But my hunch tells me that they are only loosely correlated, if at all. I suspect that the four possible combinations of political left/right and software left/right each contain around a quarter of the programming population, plus or minus maybe 8%. But that's a totally nonscientific hunch, and I'd love to see the real numbers.</p><p>The reason I suspect they're only loosely correlated is that I think a person's software-political views are largely formed by two forces:</p><ol><li><p>Their personal early/formative experiences as a programmer -- in particular, what kinds of decisions either burned them or made them wildly successful.</p></li><li><p>The software-political core values of their teachers, professors, mentors, role models, and coworkers.</p></li></ol><p>But there are some factors that could increase correlation between political orientation and software orientation. One is geographic/regional influence -- for instance, big tech universities being located primarily in red or blue states. Another might be basic personality traits that may incline people toward traditionally liberal or conservative value systems.</p><p>Regardless of how correlated they are, there are going to be a lot of programmers out there who are mildly shocked to find that they are political conservatives but software liberals, or vice-versa.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"isnt-the-2-party-system-a-flawed-model\">Isn't the 2-party system a flawed model?<a href=\"#isnt-the-2-party-system-a-flawed-model\" class=\"hash-link\" aria-label=\"Direct link to Isn't the 2-party system a flawed model?\" title=\"Direct link to Isn't the 2-party system a flawed model?\">​</a></h4><p>Well, yeah. Mapping something as nuanced as political viewpoints onto a one-dimensional axis is a well-known gross oversimplification. For instance, some U.S. voters may self-identify as fiscally conservative but socially liberal, and as a result may wind up not having a strong tie to either majority political party. These \"swing votes\" tend to be the reason that political moderates more often wind up winning elections, even though studies have shown that people tend to assign more credibility to extreme opinions.</p><p>The influence of centrists or moderates is just as important in the software engineering world, and we'll explore it more in a bit.</p><p>But having a single axis still allows for a wide distribution on both the right and the left. As in real politics, a moderately right-leaning viewpoint may seem dishearteningly liberal to someone who lies even further toward the extreme right. I will illustrate with some well-known real-world programming language scenarios in the examples section.</p><p>All in all, despite its oversimplifications, I think 2-party model is a good starting point for educating people about the politics of software. It'll be a good frame of reference for refining the model down the road, although that's out of scope for this essay.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"so-whats-a-software-liberal-slash-conservative\">So what's a Software Liberal slash Conservative?<a href=\"#so-whats-a-software-liberal-slash-conservative\" class=\"hash-link\" aria-label=\"Direct link to So what's a Software Liberal slash Conservative?\" title=\"Direct link to So what's a Software Liberal slash Conservative?\">​</a></h4><p>If you ignore specific real-world issues, and just focus on the underlying traits and values of real-world conservatives and liberals, it boils down to just a few themes. I'll argue that those underlying themes are also the basis for software politics.</p><p>It's easiest to talk first about conservatives, and then define liberals in terms of what conservatives are not. This is because conservatives tend to have a unified and easily-articulated value system, whereas liberals tend to be more weakly organized and band together mostly as a reaction to conservatism. This applies to both real-world and software-world politics.</p><p>So we'll start with an operational definition of conservatism, from Jost et al.:</p><blockquote><p>\"We regard political conservatism as an ideological belief system that is significantly (but not completely) related to motivational concerns having to do with the psychological management of uncertainty and fear.\"</p></blockquote><p>This theory is explored, re-tested and affirmed in a 2008 study from Garney et. al, \"The Secret Lives of Liberals and Conservatives: Personality Profiles, Interaction Styles, and the Things They Leave Behind\".</p><p>I hope you'll agree that this definition is minimally controversial. After all, the adjective \"conservative\" is more or less synonymous with caution and risk aversion. Financial conservatism is frequently (and intuitively) associated with age and with wealth. Companies tend to grow more conservative with age as they weather the storms of lawsuits, technical fiascoes, dramatic swings of public opinion, and economic downturns. We even have fables about ants and grasshoppers to teach us about conserving food for the upcoming winter.</p><p>Conservatism, at its heart, is really about risk management.</p><p>Similarly, liberal views are often associated with youth, with idealism, with naivete. In the corporate world, we think of startups as being prototypically liberal -- in part because they're setting out to change the world in some way (and liberalism is traditionally associated with change), and in part because they have to go all-out in order to hit their scheduled funding milestones, which can justify cutting corners on software safety.</p><p>Liberalism doesn't lend itself quite as conveniently to a primary root motivation. But for our purposes we can think of it as a belief system that is motivated by the desire above all else to effect change. In corporate terms, as we observed, it's about changing the world. In software terms, liberalism aims to maximize the speed of feature development, while simultaneously maximizing the flexibility of the systems being built, so that feature development never needs to slow down or be compromised.</p><p>To be sure, conservatives think that's what they're maximizing too. But their approach is... well, conservative. Flexibility and productivity are still motivators, but they are not the primary motivators. Safety always trumps other considerations, and performance also tends to rank very highly in the software-conservative's value system.</p><p>The crux of the disagreement between liberals and conservatives in the software world is this: how much focus should you put on safety? Not just compile-time type-safety, but also broader kinds of \"idiot-proofing\" for systems spanning more than one machine.</p><p>Let's characterize this core disagreement with some example statements that would be rated with much higher importance by conservatives than by liberals:</p><ol><li><p>Software should aim to be bug free before it launches. (Banner claim: \"Debugging Sucks!\") Make sure your types and interfaces are all modeled, your tests are all written, and your system is fully specified before you launch. Or else be prepared for the worst!</p></li><li><p>Programmers should be protected from errors. Many language features are inherently error-prone and dangerous, and should be disallowed for all the code we write. We can get by without these features, and our code will be that much safer.</p></li><li><p>Programmers have difficulty learning new syntax. We should limit the number of languages used at our company, so that nobody has to learn a new syntax when a system goes down in the middle of the night on Christmas Eve. And we should never permit features that allow defining new syntax, nor changing the semantics of existing syntax. (Common examples: no operator overloading, and NO metaprogramming!)</p></li><li><p>Production code must be safety-checked by a compiler. Any code that cannot be statically checked should in general be avoided. In specific cases where it is strictly necessary, uses of it must be approved by a central committee. (Examples: eval, dynamic invocation, RTTI).</p></li><li><p>Data stores must adhere to a well-defined, published schema. Relational databases must be in third-normal form and adhere to a UML or equivalent schema definition. XML should have a DTD. NoSQL databases and name/value stores -- both of which should be avoided in general -- must have a separate physical schema that defines all permissible keys and their corresponding value types.</p></li><li><p>Public interfaces should be rigorously modeled. Data should never be stored in strings or untyped collections. All input and output entities should be thorougly and explicitly specified via statically-checkable, ideally object-oriented models.</p></li><li><p>Production systems should never have dangerous or risky back-doors. It should never be possible to connect to a live production system via a debugger, telnet shell, nor any other interface that allows the developer to manipulate the runtime operation of the code or data. The only ports into a production system should be read-only monitoring channels.</p></li><li><p>If there is ANY doubt as to the safety of a component, it cannot be allowed in production -- no matter how teams may cry and wail that they need it to make forward progress. (I'm talkin' to you, FUSE).</p></li><li><p>Fast is better than slow. Everyone hates slow code. Code should perform well. You should engineer all your code for optimum speed up front, right out of the box. Otherwise it might not be fast enough. Avoid using languages or DSLs or libraries that have a reputation for being slow.  Even if they're fast enough for your current purposes, the requirements (or callers) could change, and suddenly the software would be too slow!</p></li></ol><p>Of course these examples are meant to be illustrative rather than comprehensive. And obviously not all of them may necessarily be espoused by everyone who self-identifies as a conservative. But barring minor exceptions, they are all very common Conservative viewpoints.</p><p>To decide whether a system or a technology is liberal or conservative, just think of a bunch of independent \"safety checkboxes\". If a majority of them are checked, then the system is conservative as a whole. The more that are checked, the more conservative it is.</p><p>By way of comparison, here are the Liberal versions of the nine examples above. It might help to think of Scooby-Doo characters. The examples above are all from Fred, and the examples below are from Shaggy.</p><ol><li><p>Bugs are not a big deal. They happen anyway, no matter how hard you try to prevent them, and somehow life goes on. Good debuggers are awesome pieces of technology, and stepping through your code gives you insights you can't get any other way. Debugging and diagnosing are difficult arts, and every programmer should be competent with them. The Christmas Eve Outage scenario never, ever happens in practice -- that's what code freeze is for. Bugs are not a big deal! (This belief really may be the key dividing philosophy between Conservative and Liberal philosophies.)</p></li><li><p>Programmers are only newbies for a little while. The steady state for a programmer's career is being smart, knowledgeable, creative, resourceful and experienced. Putting a bunch of rules in place to protect newbies from doing harm (or being harmed) is incorrectly optimizing for the transient case instead of the steady state.</p></li><li><p>Programmers figure stuff out amazingly fast when their jobs depend on it. People learn to read sheet music, braille, sign language, and all sorts of other semiotic frameworks. Hell, even gorillas can apparently do all that. Programmers don't need protection from syntax. They just need documentation and a little slack time to read up on it.</p></li><li><p>Succinctness is power. Code should be kept small. Period. If your static checking tools can't reason about the code, then the checking needs to be made smarter (e.g. by incorporating runtime data) rather than making the code dumber.</p></li><li><p>Rigid schemas limit flexibility and slow down development. Lightweight/partial/optional schemas are a better tradeoff. Moreover, the schema is often not well-understood until a lot of data is collected and a lot of use cases are thorougly exercised. So the schema should follow the code rather than precede it.</p></li><li><p>Public interfaces should above all else be simple, backward-compatible, and future-compatible. Rigorous modeling is just guessing at how the interface will need to evolve. It makes both forward- and backward-compatibility almost impossible, resulting in interface churn and customer unhappiness. Public interfaces should always do the simplest thing that could possibly work, and grow only as needed.</p></li><li><p>System flexibility can mean the difference between you getting the customer (or contract) vs. your competitor nabbing it instead. Security and safety risks in runtime production systems can be mitigated and controlled by logging, monitoring and auditing. There are plenty of existence-proofs of large systems with root-access backdoors and shells (e.g. RDBMS, online game servers) whose risk is controlled while still giving them world-class runtime flexibility.</p></li><li><p>Companies should take risks, embrace progress, and fiercely resist ossification. It doesn't matter how big your business is: it must grow or die. If you want to stay competitive, you have to make a conscious, often painful effort to take risks. Which means you'll need good recovery techniques for the inevitable disasters. But you need those even if you don't take risks.  So take risks!</p></li><li><p>Premature optimization is the root of all evil. Get the code working first, focusing on correctness over performance, and on iterative prototyping over correctness. Only when your customers list latency as the top priority should you begin performing profiler-driven optimizations.</p></li></ol><p>There you have it: Shaggy vs. Fred.</p><p>Just as in real-world politics, software liberals are viewed by conservatives as slovenly, undisciplined, naive, unprincipled, downright \"bad\" engineers. And liberals view conservatives as paranoid, fearmongering, self-defeating bureaucrats.</p><p>Once again, although I don't think the two camps will ever agree, I do think that mutual understanding of the underlying value systems may help the camps compromise.</p><p>Or at the very least, the conservative/liberal classification should help the two camps steer clear of each other. I think it is probably better to have a harmonious team of all-liberals or all-conservatives than a mixed team of constantly clashing ideologies. It's a lot like how vehicle-driving philosophies can differ regionally -- it's OK if everyone drives in some crazy way, as long as they ALL drive that way.</p><p>So Security Engineers are the most conservative, then, right?</p><p>Wrong! The reality here goes against intuition, and I mention it to show how poorly our intuition fares when it comes to interpolating software-political views from job descriptions.</p><p>Security engineers are deeply concerned with risk assessment and attack-surface management, so you might well guess that they would naturally tend towards software conservatism.</p><p>In practice, however, security engineers tend to be keenly aware of the tradeoffs between caution and progress, since they have to spend big parts of their day meeting with software teams who are often too busy (not to mention woefully underinformed) to spend a lot of time on security. Security engineers learn quickly to make well-informed and practical risk-management decisions, rather than conservatively always trying to be as safe as humanly possible.</p><p>Hence many security engineers are in fact software liberals. Some just swing that way.</p><p>Many of the security engineers at Google happen to be fans of Ruby -- both as an intrinsically secure language, and also as a nice, expressive language for writing auditing scripts and other security analysis tools. It wound up being fairly easy for me to get security sign-off for using Ruby in production for my first project at Google. In contrast, it was almost career-endingly difficult for me to get the same sign-off from our highly conservative systems programmers.</p><p>The reality is that almost any programming specialization is going to be divided into liberal and conservative camps. There are left- and right-wing versions of web developers, persistence engineers, protocol engineers, serving-system engineers, and most other sub-genres of programming.</p><p>I'm hard-pressed to think of any domains that tend to be mostly-liberal or mostly-conservative, with the sole exception of Site Reliability Engineering, which is sort of by definition a conservative role. For most of the other domains that initially came to mind -- for instance, data analysts, which at first I thought were uniformly liberal -- I've been able to think of specific teams or subdomains composed entirely of one or the other.</p><p>So in the end I think it comes down to personal preference. That's it. I don't think it's as domain-driven as much as it is personality-driven. Some people are just liberal, and some are just conservative, and that's how they are.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"big-fat-disclaimer\">BIG FAT DISCLAIMER<a href=\"#big-fat-disclaimer\" class=\"hash-link\" aria-label=\"Direct link to BIG FAT DISCLAIMER\" title=\"Direct link to BIG FAT DISCLAIMER\">​</a></h4><p>Before I continue, I will now issue the important disclaimer that I am a hardcore software liberal, bordering on (but not quite) being a liberal extremist.</p><p>This fact, by and large, is what has driven me to pen many of my most infamous and controversial rants, including (among others) \"Execution in the Kingdom of Nouns\", \"Portrait of a N00b\", \"Rhinos and Tigers\", \"Code's Worst Enemy\", \"Scheming is Believing\", \"Tour de Babel\", \"Dynamic Languages Strike Back\", \"Transformation\", \"Haskell Researchers Announce Discovery of Industry Programmer Who Gives a Shit\", and many others besides.</p><p>Sure, I sometimes write about other stuff. My in-progress \"Programmers View of the Universe\" series is motivated by an entirely different bugbear that has nothing at all to do with software politics. My \"Universal Design Pattern\" article is about a technique that transcends software politics and can be applied with equal effectiveness in either a conservative or a liberal manner. And I've written about video games, Japanese anime and random other stuff.</p><p>But I've come to understand that liberalism underlies a tremendous amount of my thinking. Even when I'm writing about management, I see that I am a liberal manager rather than a conservative one. And despite being both relatively old and relatively wealthy, I am also a political liberal -- both socially and fiscally.</p><p>Nevertheless I am going to try to represent both sides fairly and accurately in this essay. You know, mostly. I think it's most important for you to buy in to the left/right distinction itself. I think it's far less important whether you happen to agree with my particular side.</p><p>I'll consider this essay a success if a sufficient number of you agree that the liberal/conservative distinction is valid for software engineering, and that my particular left/right classification of various technologies and philosophies below seems intuitively reasonable. I'm fine with declaring success if we disagree on a few small details, as long as we agree on the overall picture.</p><p>Soooo... static typing enthusiasts are conservative, right?</p><p>Why yes. Yes, they are. Static typing is unquestionably one of the key dividing software-political issues of our time. And static typing is a hallmark of the conservative world-view.</p><p>In the conservative view, static typing (whether explicit or inferred) is taken on faith as an absolute necessity for modern software engineering. It is not something that one questions. It is a non-issue: a cornerstone of what constitutes the very definition of Acceptable Engineering Practice.</p><p>In the liberal's view, static typing is analogous to Security Theater. It exists solely to make people feel safe. People (and airports) have proven time and again that you can be just as statistically secure without it. But some people need it in order to feel \"safe enough\".</p><p>That's a pretty big difference of opinion -- one I'm sure you can relate to, regardless of how you feel about it.</p><p>I'm not going to try to defend my view here, since that's what all those old blog posts were for. If I haven't convinced you by now, then there's not much point in continuing to try. I respect your opinion. Well, now. And I hope you now have a little better understanding of mine.</p><p>I should, however, mention that there is an unrelated (i.e. politically-neutral) point on which both camps agree: namely, that static types yield better toolchain support. This is undeniably true today, and I have made it my life's work to ensure that it is not true tomorrow.</p><p>I have spent the last four years championing an initiative within Google called the \"Grok Project\", one that will at some point burst beyond our big walled garden and into your world. The project's sole purpose in life is to bring toolchain feature parity to all languages, all clients, all build systems, and all platforms.</p><p>(Some technical details follow; feel free to skip to the next section heading...)</p><p>My project is accomplishing this lofty and almost insanely ambitious goal through the (A) normative, language-neutral, cross-language definitions of, and (B) subsequent standardization of, several distinct parts of the toolchain: (I) compiler and interpreter Intermediate Representations and metadata, (II) editor-client-to-server protocols, (III) source code indexing, analysis and query languages, and (IV) fine-grained dependency specifications at the level of build systems, source files, and code symbols.</p><p>OK, that's not the whole picture. But it's well over half of it.</p><p>Grok is not what you would call a \"small\" project. I will be working on it for quite some time to come. The project has gone through several distinct lifecycle phases in its four years, from \"VC funding\" to \"acceptance\" to \"cautious enthusiasm\" to \"OMG all these internal and even external projects now depend critically on us.\" Our team has recently doubled in size, from six engineers to twelve. Every year -- every quarter -- we gain momentum, and our code index grows richer.</p><p>Grok is not a confidential project. But we have not yet talked openly about it, not much, not yet, because we don't want people to get over-excited prematurely. There is a lot of work and a lot of dogfooding left to do before we can start thinking about the process for opening it up.</p><p>For purposes of this essay, I'll assert that at some point in the next decade or so, static types will not be a prerequisite for world-class toolchain support.</p><p>I think people will still argue heatedly about type systems, and conservatives may never be able to agree amongst themselves as to which type system approach is best for modeling program behavior. But at least I will have helped that discussion be ONLY about representations. The quality of the corresponding developer tools should no longer be a factor in the discussions.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"dividing-up-the-space\">Dividing up the Space<a href=\"#dividing-up-the-space\" class=\"hash-link\" aria-label=\"Direct link to Dividing up the Space\" title=\"Direct link to Dividing up the Space\">​</a></h2><p>I'm going to go through and toss a bunch of random technologies, patterns, designs and disciplines each into one of six buckets: \"apolitical\", \"conservative\", \"centrist\", \"liberal\" buckets, plus two buckets that start Centrist and head Left or Right in the presence of overuse. One bucket per thingy. Hey, it's not an exact science. But it should help set the rough foundation for the more detailed use cases below.</p><p>Non-political Stuff: Algorithms, data structures, concrete mathematics, complexity analysis, information theory, type theory, computation theory, and so on. Basically all CS theory. These disciplines occasionally inspire tempest-in-a-teapot butthurtedness in academia, but when it happens, it's just similar fish in too small a tank biting on each other. It's to be expected. Overall, these essentially mathematical disciplines are timeless, and they are all equally applicable to both the Liberal and Conservative programming worlds.  Yes, even type theory.</p><p>Conservative Stuff: Provably sound type systems. Mandatory static type annotations. Nonpublic symbol visibility modifiers (private/protected/friend/etc.). Strict, comprehensive schemas. all-warnings-are-errors. Generics and templates. Avoidance of DSLs (XPath, regexps) in favor of explicit DOM manipulation and hand-rolled state machines.  Build dependency restrictions.  Forced API deprecation and retirement.  No type equivalence (i.e. no automatic conversions) for numeric types.  Checked exceptions.  Single-pass compilers.  Software Transactional Memory.  Type-based function overloading.  Explicit configuration in preference to convention. Pure-functional data structures.  Any kind of programming with the word \"Calculus\" in it.</p><p>Centrist (or flat-out Neutral) Stuff: Unit testing. Documentation. Lambdas. Threads. Actors.  Callbacks.  Exceptions.  Continuations and CPS.  Byte-compilation.  Just-in-time compilation.  Expression-only languages (no statements).  Multimethods. Declarative data structures.  Literal syntax for data structures.  Type dispatch.</p><p>Liberal Stuff: Eval. Metaprogramming. Dynamic scoping. all-errors-are-warnings. Reflection and dynamic invocation. RTTI. The C preprocessor. Lisp macros. Domain-specific languages (for the most part). Optional parameters. Extensible syntax. Downcasting.  Auto-casting.  reinterpret_cast.  Automatic stringification.  Automatic type conversions across dissimilar types.  Nil/null as an overloaded semantic value (empty list, empty string, value-not-present).  Debuggers.  Bit fields.  Implicit conversion operators (e.g. Scala's implicits).  Sixty-pass compilers.  Whole-namespace imports.  Thread-local variables.  Value dispatch.  Arity-based function overloading.  Mixed-type collections.  API compatibility modes.  Advice and AOP.  Convention in preference to explicit configuration.</p><p>Centrist Stuff that Becomes Conservative If Taken Far Enough: Type modeling. Relational modeling. Object modeling. Interface modeling. Functional (i.e., side-effect-free) programming.</p><p>Centrist Stuff that Becomes Liberal if Taken Far Enough: Dynamic class loading and dynamic code loading. Virtual method dispatch. Buffer-oriented programming.</p><p>Woah, that exercise was surprisingly fun! It's far from complete, but hopefully you get the idea.</p><p>Some natural themes arise here:</p><p> -- implicit is generally liberal; explicit is generally conservative.</p><p> -- performance-oriented is generally conservative; late-optimized is generally liberal.</p><p> -- compile-time binding is generally conservative; runtime/late binding is generally liberal.</p><p> -- concurrency and parallelism in general seem to be politically charged topics, but the disagreement is orthogonal to the liberal/conservative camps.</p><p>I'd love to keep going with the classification. But I'll stop here, since we've got higher-level stuff to discuss.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"examples-and-case-studies\">Examples and Case Studies<a href=\"#examples-and-case-studies\" class=\"hash-link\" aria-label=\"Direct link to Examples and Case Studies\" title=\"Direct link to Examples and Case Studies\">​</a></h2><p>I'll walk you through a bunch of examples to show you how just widespread and deep-rooted this political phenomenon is.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"example-1-languages\">Example 1: Languages<a href=\"#example-1-languages\" class=\"hash-link\" aria-label=\"Direct link to Example 1: Languages\" title=\"Direct link to Example 1: Languages\">​</a></h4><p>Here are some very rough categorizations. Note that within each language camp there are typically liberal and conservative sub-camps. But as a whole, language usage tends to be dominated by what the language makes possible (and easy), so the culture tends to follow the features.</p><p>This list is just a few representative examples to give you the flavor. I'm only listing general-purpose languages here, since DSLs and query languages are typically feature-restricted enough to be hard to categorize.</p><p>Assembly language: Batshit liberal.</p><p>Perl, Ruby, PHP, shell-script: Extremist liberal.</p><p>JavaScript, Visual Basic, Lua: Hardcore liberal.</p><p>Python, Common Lisp, Smalltalk/Squeak: Liberal.</p><p>C, Objective-C, Scheme: Moderate-liberal.</p><p>C++, Java, C#, D, Go: Moderate-conservative.</p><p>Clojure, Erlang, Pascal: Conservative.</p><p>Scala, Ada, OCaml, Eiffel: Hardcore conservative.</p><p>Haskell, SML: Extremist conservative.</p><p>These are my own categorizations based on my own personal experiences with these languages and their respective communities. Your mileage may vary. However, I'd be quite surprised if you chose to move any of these languages more than a step or two away from where I've positioned it.</p><p>One thing that jumps out is that a language doesn't have to be statically-typed or even strongly-typed in order to be conservative overall. More on that in a bit.</p><p>The next thing you might notice from the list is that the liberal and moderate languages are all pretty popular, and that popularity declines sharply as languages head into conservative territory.</p><p>I think this has a simple explanation: It's possible to write in a liberal language with a conservative accent, but it's very hard (and worse, discouraged) to write in a conservative language with a liberal accent.</p><p>For instance, it's straightforward to write JavaScript code in a way that eschews reflection, eval, most automatic type casting, prototype inheritance, and other dynamic features. You can write JavaScript that plods along as unadventurously as, say, Pascal. It doesn't have all the static type annotations, but you can replace them with assertions and unit tests and stereotypically stolid code organization.</p><p>But if you try writing your Haskell code with a bunch of dynamic features, well, you're in for a LOT of work. Haskell enthusiasts have managed to implement dynamic code loading and a ton of other ostensibly dynamic features, but it was only through herculean effort.</p><p>What's more, if you write your liberal-language code in a conservative way, people will just look at it and say: \"Well, it's kinda boring, and you could have saved a lot of coding by using some dynamic features. But I guess it gets the job done. LGTM.\"</p><p>Whereas if you write your conservative-language code in a liberal way, you run the risk of being ostracized by your local language community, because... why are you doing all that dangerous dynamic stuff in the first place? I'll explore this cultural phenomenon further when I talk about Clojure below.</p><p>The last big, interesting observation from the list is that a lot of the most popular languages out there are only moderately conservative -- even if they think of themselves as quite conservative compared to their ultra-dynamic cousins.</p><p>I've said it before, and it bears repeating here: the reason C++, C# and Java have been particularly successful in the marketplace is that -- just like effective politicians -- they know how to play both sides.</p><p>C++ allows liberal-biased programmers to program in straight C, and it allows conservative-biased programmers to layer in arbitrary amounts of static type modeling, depending on how much work they want to expend in order to feel secure. Java?  Pretty much the same story.</p><p>Playing to both the fast-and-loose and lock-your-doors mindsets has proven to be a key ingredient to market success.  Also marketing, but it helps a LOT to be viewed as philosophically friendly by both the liberal and conservative camps.</p><p>There is a new crop of languages on the horizon (for instance, Google's Dart language, but also new specs for EcmaScript) that are deliberately courting the centrist crowd -- and also delicately playing to grab both the liberals and conservatives -- by offering optional static types. In principle this is a sound idea. In practice I think it will come down to whether the marketing is any good.  Which it probably won't be.</p><p>Language designers always seem to underestimate the importance of marketing!</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"example-2-tech-corporations\">Example 2: Tech corporations<a href=\"#example-2-tech-corporations\" class=\"hash-link\" aria-label=\"Direct link to Example 2: Tech corporations\" title=\"Direct link to Example 2: Tech corporations\">​</a></h4><p>Just for fun, let's contrast four similar-ish tech companies in their software-political outlook.</p><ol><li><p>Facebook -- Diagnosis: Extremist Liberal. Despite their scale, they are still acting like a startup, and so far they've been getting away with it. They use primarily C++ and PHP, and they're prone to bragging about how their code calls back and forth from PHP to C++ and back into PHP, presumably bottoming out somewhere. Their datastore is memcached: just name-value pairs.  No schema. They dump the data and logs into a backend Hive store and run Hadoop mapreduces for offline data analysis.  They still hold all-night hackathons every other week or so, which will remain feasible for them as long as the majority of their programmers are very young males (as was the case last time I toured there) and their stock continues to promise great riches (as was not so much the case last I checked.)  As a company they are tightly knit and strongly biased for action, placing a high value on the ability of individual programmers to launch features to their website with little to no bureaucracy or overhead.  This is pretty remarkable for a company as big as they are, with as many users as they have.  Conservatives no doubt regard them with something between horror and contempt.  But Facebook is proving that programmers of any world-view can get a hell of a lot accomplished when they gang up on a problem.</p></li><li><p>Amazon.com -- Diagnosis: Liberal. Which is surprising, given how long they've been in business, how much money is at stake, how mature their Operations division is, and how financially conservative they are. But \"Liberal\" is actually quite a retreat compared to their early days. Back in 1998-1999 they were almost exactly like Facebook is today, with the sole exception that they put everything in relational databases and did a ton of up-front relational data modeling.  Well, except in Customer Service Apps, where we used a name/value store just to be flexible enough to keep up with the mad chaotic scramble of the business launches.  All part of my multi-decade indoctrination as a Liberal.  In any case, despite many corporate improvements with respect to work-life balance (which happened after several stock plunges and years of significant double-digit turnover in engineering), Amazon has retained its liberal, startup-like engineering core values.  Every team owns their own data and makes their own decisions, more or less like independent business units.  Amazon still launches and executes faster than just about anyone else out there, because they're still willing to take real risks (incurring occasional huge outages), and to make hard decisions in favor of launching early and often.  Above all else, Amazon has proven conclusively that after fifteen years, they can still innovate like nobody else.  They've still got it.</p></li><li><p>Google -- Diagnosis: Conservative. They began life as slightly liberal and have grown more conservative ever since. Google was only software-liberal in the very very early days, back when the search engine itself was written in Python. As they grew, they quickly acquired a software conservatism driven entirely by the engineers themselves.  Manifestos were written about the dangers of using multiple languages, and strict style guides were put in place to severely limit \"risky\" or \"hard to read\" language features of the few languages they did allow.  Google's JavaScript code is written in an extremely conservative style with extensive static type annotations, and eval is forbidden.  The Python style guide forbids metaprogramming and other dynamic features, which makes their Python look a lot like untyped Java.  And they have severely limited the use of many C++ language features, with C++11 support rolling out literally one feature every few weeks.  (There are over five hundred new features in C++11.)  In internal surveys, Google engineers commonly cite bureaucracy, churn and complexity as core obstacles to feature advancement and rapid launches.  Google has made serious attempts on several occasions to reduce this bureacracy, but they always get pushback from -- surprise -- the engineers themselves, who have grown so staunchly conservative that they actively (and even more often, passively) resist the introduction of more flexible stacks and technologies.  Most of the major technological shifts within Google over the past half-decade have been overtly conservative.  For a liberal like me, it has been a very sad process to observe.  But at least I've found myself a niche that's widely regarded (by both camps) as valuable, and within my own org we can still be pretty liberal and get away with it.</p></li><li><p>Microsoft -- Diagnosis: Batshit Conservative. Microsoft has two geese that lay golden eggs: Office and Windows. Microsoft has been reduced to a commercial farmer protecting the geese from all incursions.  The golden eggs still have value, because customers are locked into the platform by the cost-ineffectiveness of retraining their fleets.  But Microsoft can no longer innovate in Office or Windows precisely because of those corporate fleet retraining costs.  Their OEMs are stretched as thin as they can go.  Apple is dominating the handheld markets, and Microsoft is actively stifling their own innovation in Windows Phone because they're afraid it will cannibalize their core Windows business.  Microsoft has not had a successful product-level innovation in fifteen, maybe twenty years.  All of their successful products have been copies of competitors' products:  IE, XBox, C#, .NET, Bing, Windows Phone, and so on ad infinitum. All great implementations of someone else's ideas. Microsoft's playbook is to embrace, extend, and leverage their brand to crush the competition -- or at least it was, until the goverment put an end to that circa 2002.  Now the company genuinely doesn't know what the fuck to do with themselves, and what's more, instead of Bill Gates they now have a lunatic in charge.  Employees are leaving in droves, all citing the same internal \"existential crisis\" and unbearable corporate politics caused by competing business units actively sabotaging one another.  Microsoft has turned into a caricature of right-wing corporatism: sitting on their front porch with a shotgun cursing at passers-by, waiting for their government bribes to give them another few years of subsidies and shelters while they wait to die.  I've personally chatted with close to four hundred current and ex-Microsoft employees over the past seven years.  Oh, the stories I could tell you... someday, maybe.</p></li><li><p>Bonus company: Apple. Diagnosis: no idea, but they're so good at marketing that it's almost irrelevant. Would love to have more insight into their internal software culture, though.  Any takers?  Throwaway accounts?  AMA?</p></li></ol><p>OK, that was a fun exercise too. But we need to move on! Almost done now.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"specific-case-study-the-clojure-language\">Specific Case Study: The Clojure Language<a href=\"#specific-case-study-the-clojure-language\" class=\"hash-link\" aria-label=\"Direct link to Specific Case Study: The Clojure Language\" title=\"Direct link to Specific Case Study: The Clojure Language\">​</a></h4><p>I've been meaning to follow up on Clojure for a while now. Over a year, at least. But until recently I didn't have the conceptual tools to explain what I wanted to say about it.</p><p>Now I do!</p><p>Clojure is a new-ish Lisp dialect that runs on the JVM and .NET, and I was honored to write the Foreward to \"The Joy of Clojure\" a while back. For a few years I had been really excited to start learning Clojure, and my initial experiences with it were quite positive.</p><p>However, I eventually learned that the Clojure community is extremely conservative. That is is pretty unusual for a Lisp dialect. Lisp is widely regarded as one of the most liberal language families in existence. And Clojure has the superficial appearance of being a laissez-faire kind of language. It is quite expressive, including a -- ahem -- liberal dose of new syntax. And it eschews static type annotations and strong type modeling in favor of a small set of highly regular, composable data types and operations -- not unlike, say, Scheme or Python.</p><p>But the resemblance to a liberal language ends there. Clojure's community came pre-populated with highly conservative programmers from the pure-functional world: basically Haskell/ML types (lots of puns today!) who happen to recognize the benefits of Lisp's tree syntax. So under its expressive covers, everything about Clojure is strongly conservative, with a core overriding bias towards protecting programmers from mistakes.</p><p>And the community follows suit. At a Clojure conference last year (or was it two years ago? time flies so fast these days...), there was a key presenter doing a talk on how Macros were basically harmful and should be avoided in modern Clojure code.</p><p>I trust that if you know anything about Lisp, your blood is basically boiling at this point. I know mine was.</p><p>But his argument is perfectly valid from the classic software-conservative's viewpoint. Macros allow you to invent domain-specific language abstractions. Those require documentation in order for users to figure out what they do, and what they mean. That means you can know Clojure and not really know enough to read someone's Clojure code without some documentation handy. Which is sort of the definition of a newbie. And there you have it. In a very real sense, conservatives fear being turned -- in the blink of an eye -- from masters into newbies by the application of macros.</p><p>It's sort of scary if you have a lot of your personal identity invested in knowing some shit. And wouldn't you know it, real-world politics conservatives are shown in study after study to be \"staunch\" in sticking to their world views rather than compromising. That means they have a lot of identity tied up in those views.</p><p>So while I liked a lot of what I saw in Clojure, as a hardcore software liberal I was inevitably and inexorably driven away from the language. And that's good for me, and it's good for Clojure. I mean, why should they compromise?</p><p>I think that my conceptual framework gives us an \"out\" -- a way to avoid being emotional about these subjects. Casting the problem as a clash between Liberalism and Conservsatism gives us the ultimate ticket for \"agreeing to disagree\".</p><p>Hopefully it will also help language designers and communities do a better job of targeted marketing. Right now just about every language out there makes a claim along the lines of \"This language is the best choice for everybody!\" But now we know that is very unlikely to be true -- or if it is, we can at least be assured that they're centrists, and they run the risk of being equally distasteful to everybody.</p><p>In the conservative/liberal framework, language designers can make more accurate, less bait-and-switchy claims; for instance: \"Haskell is the best choice for every radical extremist conservative programmer!\"</p><p>Well, we can work on the wording. But you get the idea.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"wrap-up\">Wrap-Up<a href=\"#wrap-up\" class=\"hash-link\" aria-label=\"Direct link to Wrap-Up\" title=\"Direct link to Wrap-Up\">​</a></h2><p>I was thinking of going through a bunch more examples and stuff, but I see that I'm on my third (Editor's Note:  fourth) glass of wine, which means my typing is about to give out any minute.</p><p>So let's wrap it up!</p><p>There's one kinda key point I wanted to get across, but didn't see a good place for it. That point is this: please do not be alarmed that I am calling you a (software) Conservative.</p><p>I worry that politically left-leaning programmers will hear the term \"conservative\" and will immediately associate it with all of the... uh, politically-charged connotations associated with far right-wing conservatism in the United States political arena today. You know, racism, sexism, religious fundamentalism, homophobia, warmongering, bear-shooting, that kind of thing.</p><p>I'm not saying they're bad, at least not in this essay. I'm just saying nobody in their right mind wants to be associated even remotely with those embarrassing wingnuts. See what fine-grained, nuanced distinctions three (Editor's Note: four) glasses of wine can produce? But I'm not saying their views are bad. No. Not here.  I'm just observing that they're heavily politically charged viewpoints which have, for better or worse, recently come to be associated with the term \"conservatism\" in US politics.</p><p>So please do me a favor and try to dissociate those specific agendas and real-world political viewpoints from the generic term \"Conservative\", which here really just means \"risk averse\".</p><p>It's perfectly OK, and normal, to be a programming conservative. You don't have to shoot any bears. I would actually like to see the terms \"liberal\" and \"conservative\" to become badges of honor in the programming world. People should stand behind their beliefs. I mean, we do already, I think, so it shouldn't be much of a stretch to allow our beliefs to be given convenient labels.</p><p>Ah, me. I can see the Euphemism Treadmill rearing its ugly head already. We'll see.</p><p>Anyway, tell me what you think! I welcome any and all viewpoints and comments. Even from bears!</p><p>Special thanks to Writer's Block Syrah for reviewing this post for career suicide.</p><p>(Special Note to my fellow Googlers:  Yes, I meant to post this externally.  BOTH times.  No, I am not the Mouth of Sauron.)</p>",
            "url": "https://whoislewys.com/blog/2019/09/06/Notes-From-The-Mystery-Machine_Bus",
            "title": "Notes from the Mystery Machine Bus",
            "summary": "A mirror of Steve Yegge's old Google+ post",
            "date_modified": "2019-09-06T12:00:00.000Z",
            "author": {
                "name": "Steve Yegge"
            },
            "tags": [
                "code",
                "rant"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/2018/10/18/audio_norm_guides_are_evil_and_immoral",
            "content_html": "<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-wrong-way\">The Wrong Way<a href=\"#the-wrong-way\" class=\"hash-link\" aria-label=\"Direct link to The Wrong Way\" title=\"Direct link to The Wrong Way\">​</a></h2><p>There are SEVERAL guides online telling you how to normalize audio to provide a great listening experience by using FFmpeg's \"loudnorm\" filter. Most walk you through a simple process called \"track normalization.\" You set a goal loudness, and adjust every piece of audio up or down to match that loudness. This is evil and immoral and I'll tell you why:</p><p>Loudnorm changes the way things sound!</p><p>Loudnorm crushes dynamics!</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-right-way\">The Right Way<a href=\"#the-right-way\" class=\"hash-link\" aria-label=\"Direct link to The Right Way\" title=\"Direct link to The Right Way\">​</a></h2><p>The right way™ is \"Album Normalization,\" where every piece of audio (song, level soundtrack, etc.) gets normalized in relation to the loudest piece of audio.</p><blockquote><p>\"With album normalization, just the loudest tracks of an album are made equally loud and the other tracks keep the relative level they had on their album.\" - Eelco Grimm, loudness researcher</p></blockquote><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-you-need-good-audioloudness-normalization\">Why You Need Good Audio/Loudness Normalization<a href=\"#why-you-need-good-audioloudness-normalization\" class=\"hash-link\" aria-label=\"Direct link to Why You Need Good Audio/Loudness Normalization\" title=\"Direct link to Why You Need Good Audio/Loudness Normalization\">​</a></h2><p>If you're a developer creating a VR experience, or a game, or something where music features heavily, you need to do normalization, and you need to do it right.</p><p>You can't just normalize every piece of audio to a certain average level and call it good.</p><p>Not normalizing audio = poor user experience</p><p>There can still be jarring sound transitions. You ever watch TV at a comfy volume, realize the show is going to commercials, and feel dread flood through your body, ears recoiling in fear because you know the commercials you're about to see will be 10x as loud as the show?</p><p>Yeah, good normalization fixes that.</p><p>So unless you want your users to hate you, normalize your audio properly!</p><p>Now what happens if you normalize your audio wrong, crushing the dynamic range?</p><p>Honestly, a lot of people probably won't even notice (circa 2018).</p><p>But once good normalization becomes the standard for all experiences including audio, people WILL hear the difference between a great normalization algorithm and the loudnorm kludge copy-pasted from Stack Overflow, and they will HATE it. And don't you want your users to have the BEST experience?</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-proof-of-why-people-love-good-normalization\">The Proof of Why People Love Good Normalization<a href=\"#the-proof-of-why-people-love-good-normalization\" class=\"hash-link\" aria-label=\"Direct link to The Proof of Why People Love Good Normalization\" title=\"Direct link to The Proof of Why People Love Good Normalization\">​</a></h2><p>\"Alright tough guy,\" you say, \"what makes you so qualified to talk about audio like this?\"</p><p>Well, I'm really not. I have very little ethos here. But I'm STILL positive users love properly normalized audio.</p><p>Why?\nCause TIDAL hired a smart guy, with a ton of audio street cred, to do a study on best normalization practices. He sent non-normalized &amp; normalized versions of a playlist to 38 people. And guess what?\n80% of people preferred the album normalized versions to track normalized! <a href=\"https://octo.hku.nl/octo/repository/getfile?id=qLlZPGSVXFM\" target=\"_blank\" rel=\"noopener noreferrer\">Link to study</a>. And those 80%, 9% said they'd never accept track normalization (an inferior normalizing method) if it were turned on by default!</p><p>Ok, I admit at least one of subjects was an audio nerd, as Ian Shepherd (legendary mastering engineer) admits he was a part of the study in his <a href=\"http://productionadvice.co.uk/tidal-normalization-upgrade/\" target=\"_blank\" rel=\"noopener noreferrer\">great post on the subject</a>. Regardless, 31/38 people is a pretty strong majority. I imagine the study would translate well to a larger sample of non audio nerds as well.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"in-conclusion\">In Conclusion...<a href=\"#in-conclusion\" class=\"hash-link\" aria-label=\"Direct link to In Conclusion...\" title=\"Direct link to In Conclusion...\">​</a></h2><p>Be careful following guides online dealing with subjects you're not familiar with. It can be tempting to just do what Google's Loudness page says, without realizing they're giving recommendations for Google Assistant developers. It can be easy to copy-paste a command from Stack Overflow, when the use case that was asked about is totally different from your own. You're used to these kinds of resources, and so am I.</p><p>But when it comes to writing lines of code that affect your entire project in a domain you don't know much about, you need to reach out to someone with knowledge in the field.</p>",
            "url": "https://whoislewys.com/blog/2018/10/18/audio_norm_guides_are_evil_and_immoral",
            "title": "Why FFmpeg Loudness & Normalization Guides Are All Wrong, Evil & Immoral!",
            "summary": "The Wrong Way",
            "date_modified": "2018-10-18T06:07:00.000Z",
            "author": {
                "name": "Luis Gomez",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "DIY",
                "music",
                "audio"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/2018/10/10/the-only-4-deep-learning-frameworks-that-matter",
            "content_html": "<p>Let me get this off my chest right off the bat: Keras is all you need for 90% of neural network applications. Before jumping in, you should understand what is going on with neural networks(watch <a href=\"https://www.youtube.com/playlist?list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi\" target=\"_blank\" rel=\"noopener noreferrer\">3Blue1Brown's videos</a>).</p><p>Ideally, you can understand all the math behind them and code a basic one from scratch (read <a href=\"http://neuralnetworksanddeeplearning.com/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">Nielsen's Neural Networks and Deep Learning</a>).</p><p>But for most cases, even <em>understanding</em> is unnecessary with all the pretrained models frameworks ship with nowadays.</p><p>But, assuming you're not a n00b and you need to do more beyond just classifying 2D images or recognizing characters, here are the Deep Learning/ Neural Network frameworks can best fit your use case.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-only-4-deep-learning-frameworks-that-matter\">The Only 4 Deep Learning Frameworks That Matter<a href=\"#the-only-4-deep-learning-frameworks-that-matter\" class=\"hash-link\" aria-label=\"Direct link to The Only 4 Deep Learning Frameworks That Matter\" title=\"Direct link to The Only 4 Deep Learning Frameworks That Matter\">​</a></h2><ol><li><a href=\"https://keras.io\" target=\"_blank\" rel=\"noopener noreferrer\">Keras</a> 🅱️</li></ol><ul><li><p><strong>TLDR:</strong> Are you a researcher/startup employee that wants to <em>test ideas 10x faster</em> than the competition? Just use Keras already.</p></li><li><p>Okay okay fellow pedants - Keras is more of a <em>library</em> than a <em>framework</em>. Regardless of what you call it, it is undeniably an incredible tool. It's favored by researchers and AI startups alike for its readability and ability to rapidly create neural networks.</p><p>It works by using a framework in its backend (e.g. TensorFlow) to do all the neural network math, and wraps different functionalities for easy usage. Keras gives you high level functions to do things like create layers, apply max pooling, check accuracy, and other common neural network tasks in a single line of code.</p><p>Having these possibilities at the tips of your fingers is invaluable when trying out many different model architectures.</p><blockquote><p>pro deployment tip, you can export Keras models as TF graphs and upload them to CloudML to deploy your network in under 30 mins.</p></blockquote></li></ul><ol start=\"2\"><li><a href=\"https://caffe2.ai\" target=\"_blank\" rel=\"noopener noreferrer\">Caffe2</a> ☕</li></ol><ul><li><p><strong>TLDR</strong>: Do you need a neural network to run on <em>mobile</em>? Move Caffe2 to the top of your list.</p></li><li><p>This Facebook-supported framework offers extremely high performance with a focus on large scale deployments and mobile models. Users love it for its excellent performance on mobile devices, flexibility, and ease of writing. It is usually used through the Python API, though there is a C++ API as well. Also, a model can be written in Python then deployed in pure C++ for additional performance and scalability.</p><p>Caffe2 also comes with a TON of pretrained models form the lovely <a href=\"https://github.com/caffe2/models\" target=\"_blank\" rel=\"noopener noreferrer\">Model Zoo</a>.</p></li></ul><ol start=\"2\"><li><a href=\"https://pytorch.org\" target=\"_blank\" rel=\"noopener noreferrer\">PyTorch</a> 🔥</li></ol><ul><li><strong>TLDR:</strong> Are you a researcher? Do you need to try a complex RNN, or a net with changing dimensions? PyTorch it is.</li><li>Another Facebook-supported framework, this Python library has a heavy focus on research and dynamic computation. It is loved by researchers for the ability to create exotic, experimental neural networks (without writing Lua üòà). If you have a crazy idea that you're not sure how to implement with other frameworks, check out PyTorch. Many recent, successful research papers release their code in PyTorch. Don't let all the research talk scare you though, PyTorch still has an accessible API, great performance, and documentation.</li></ul><ol start=\"4\"><li><a href=\"https://tensorflow.org\" target=\"_blank\" rel=\"noopener noreferrer\">TensorFlow</a> 🤔</li></ol><ul><li><p><strong>TLDR:</strong> Need in-browser neural networks?</p></li><li><p>Sorry to put you at the bottom of the list, TensorFlow. The focus on parallelism and ease of integration with Google's CloudML makes TensorFlow deploying highly scalable networks easy, at the expense of typically writing more complex code. It is not as intuitive to write as the other frameworks mentioned. The main reason I list it here because of the exciting new TensorFlow.js enabling high-performance, in-browser models. It is getting better and better at mobile performance but still doesn't touch Caffe2. I think TensorFlow is worth keeping an eye on as Google continue to innovate and integrate ML into different areas, but unless you have</p><blockquote><p>if you're a beginner who needs to deploy a model, CloudML is a great option. But don't feel like you <em>have</em> to learn TensorFlow. You can write and train a model in Keras and export a TensorFlow graph and upload that.</p></blockquote></li></ul><p>There you go. In my humble opinion, these are the only 4 deep learning frameworks that matter in 2018.</p><p>Don't let yourself get into a black hole of paralysis by analysis, researching every framework endlessly. Pick one of the above. There are good tutorials for all of them. Pick one and learn it. If you need to switch over to another for some reason, the skills you learn in one will transfer to other. Just <em>start</em>!</p><blockquote><p>*there is no red K emoji, 🅱️ is the best I can do</p></blockquote>",
            "url": "https://whoislewys.com/blog/2018/10/10/the-only-4-deep-learning-frameworks-that-matter",
            "title": "The Only 4 Deep Learning Frameworks That Matter",
            "summary": "Let me get this off my chest right off the bat: Keras is all you need for 90% of neural network applications. Before jumping in, you should understand what is going on with neural networks(watch 3Blue1Brown's videos).",
            "date_modified": "2018-10-10T15:27:00.000Z",
            "author": {
                "name": "Luis Gomez",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "AI",
                "ML",
                "Code",
                "Deep Learning"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/2018/06/25/Building My Own Guitar",
            "content_html": "<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why\">Why?<a href=\"#why\" class=\"hash-link\" aria-label=\"Direct link to Why?\" title=\"Direct link to Why?\">​</a></h2><p>I wanted a faster, more efficient way to make music.</p><p>My solution? Stick a Launchpad in a guitar</p><p>Sadly, no electric guitars I found had the room for a launchpad.</p><p>My solution (pt 2)? Build my own guitar with correct dimensions.</p><p>My advice? DON'T DO IT. It was really hard and time consuming.</p><p>Unless you NEED to do this to get specific dimensions &amp; realize some crazy idea of your own,\njust buy a nice used guitar. You'll still get a lot of instrument for your money, and SAVE TIME.</p><p><em>Do not delay, sing and play everyday.</em></p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"shopping-list\">Shopping List<a href=\"#shopping-list\" class=\"hash-link\" aria-label=\"Direct link to Shopping List\" title=\"Direct link to Shopping List\">​</a></h2><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"hardware\">Hardware<a href=\"#hardware\" class=\"hash-link\" aria-label=\"Direct link to Hardware\" title=\"Direct link to Hardware\">​</a></h3><ul><li><a href=\"https://www.amazon.com/Kmise-Electric-Replacement-Mahogany-Rosewood/dp/B01KLBMRLM/ref=sr_1_1?s=musical-instruments&amp;ie=UTF8&amp;qid=1527824231&amp;sr=1-1&amp;keywords=kmise%2Bles%2Bpaul%2Bneck&amp;th=1\" target=\"_blank\" rel=\"noopener noreferrer\">Kmise Les Paul Neck</a> - $39.99</li><li><a href=\"https://www.amazon.com/Kmise-A2421-Tuning-Tuners-Machine/dp/B010FWMZ28/ref=sr_1_7?ie=UTF8&amp;qid=1527823627&amp;sr=8-7&amp;keywords=locking+tuners#customerReviews\" target=\"_blank\" rel=\"noopener noreferrer\">Tuners (Chrome-A)</a> - $13.43</li><li><a href=\"https://www.amazon.com/YMC-Electric-Guitar-Precision-Replacement/dp/B01LMDLP1Q/\" target=\"_blank\" rel=\"noopener noreferrer\">Neck Plate</a> - $5.95</li><li><a href=\"https://www.amazon.com/Elixir-Strings-Electric-POLYWEB-009-042/dp/B0002FO9EG/ref=sr_1_1?s=musical-instruments&amp;ie=UTF8&amp;qid=1527825650&amp;sr=1-1&amp;keywords=elixir+polyweb+.009\" target=\"_blank\" rel=\"noopener noreferrer\">Strings</a> - $10</li><li><a href=\"https://www.homedepot.com/s/sanded%2520plywood?NCNI-5\" target=\"_blank\" rel=\"noopener noreferrer\">Plywood</a> - $20</li><li><a href=\"https://www.amazon.com/gp/product/B010UXKLLO/ref=oh_aui_detailpage_o00_s01?ie=UTF8&amp;psc=1\" target=\"_blank\" rel=\"noopener noreferrer\">Strap Buttons</a> - $3.42</li><li><a href=\"https://www.ebay.com/itm/Compensated-Ridge-Wraparound-Combination-Bridge-Tailpiece-for-Epiphone-LP-Jr/123044682818\" target=\"_blank\" rel=\"noopener noreferrer\">Stop Tailpiece / Bridge</a> - $14.99</li><li><a href=\"https://www.amazon.com/gp/product/B076HWP6Q7/ref=oh_aui_detailpage_o00_s02?ie=UTF8&amp;psc=1\" target=\"_blank\" rel=\"noopener noreferrer\">Pots</a> - $6.98</li><li><a href=\"https://www.guitarfetish.com/Orange-Drop-Tone-Caps-022uf-for-Les-Paul_p_23072.html\" target=\"_blank\" rel=\"noopener noreferrer\">Caps</a> $ 5.95</li><li><a href=\"https://www.amazon.com/gp/product/B00UR6L67I/ref=oh_aui_detailpage_o00_s01?ie=UTF8&amp;psc=1\" target=\"_blank\" rel=\"noopener noreferrer\">Knobs</a> - $5.99</li><li><a href=\"https://www.guitarfetish.com/KP--GFS-Alnico-Vintage-Wound-Dogear-Pickups-Cream--Kwikplug-Ready_p_22018.html\" target=\"_blank\" rel=\"noopener noreferrer\">Pickup</a> - $33.95</li><li>1/4 in. Jack (I had a bag full from <a href=\"https://www.aliexpress.com/item/Wholesale-10-Pcs-Nickel-6-35mm-1-4-Inch-Mono-TS-Panel-Chassis-Mount-Jack-Audio/32800423351.html?spm=2114.search0104.3.16.42ff7d29YkRUFP&amp;ws_ab_test=searchweb0_0,searchweb201602_1_10152_10151_10065_10344_10068_10130_10324_10342_10547_10325_10343_10546_5722611_10340_10548_10341_10545_10696_5722911_5722811_5722711_10084_10083_10618_10307_10059_100031_10103_10624_10623_10622_10621_10620_5722511,searchweb201603_2,ppcSwitch_7&amp;algo_expid=f82202b0-2592-46d5-8b3d-37ce5402b93d-2&amp;algo_pvid=f82202b0-2592-46d5-8b3d-37ce5402b93d&amp;priceBeautifyAB=0\" target=\"_blank\" rel=\"noopener noreferrer\">AliExpress</a> but <a href=\"https://www.amazon.com/Switchcraft-11-Female-4-Inch-Washer/dp/B00C5B20QE/ref=sr_1_3?s=musical-instruments&amp;ie=UTF8&amp;qid=1528239216&amp;sr=1-3&amp;keywords=1%2F4+in+jack\" target=\"_blank\" rel=\"noopener noreferrer\">this</a> would ship fast) $5</li><li>22 Gauge Wire (I already had a lot. Look around <a href=\"https://www.amazon.com/Electrical-Gauge-Silicone-Cable-Black/dp/B0746HG158/ref=sr_1_1_sspa?ie=UTF8&amp;qid=1528239430&amp;sr=8-1-spons&amp;keywords=22+gauge+wire&amp;psc=1\" target=\"_blank\" rel=\"noopener noreferrer\">Amazon</a> eBay, or Ali for the cheapest) - $5</li></ul><p><strong>Total Cost: $170</strong></p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"tools\">Tools<a href=\"#tools\" class=\"hash-link\" aria-label=\"Direct link to Tools\" title=\"Direct link to Tools\">​</a></h3><ul><li>Orbital Jigsaw (or a handsaw)</li><li>T-Shank Blades</li><li>Power drill</li><li>Cheap drillbit set</li><li>13/32in. drill bit (for bridge studs)</li><li>Soldering iron</li><li>Sandpaper</li><li>File</li><li>6 3 inch clamps (used for gluing two ~1in. boards together)</li></ul><p>Thankfully my pops had most of these tools laying around. Did have get a cheap jigsaw from harbor freight.</p><p>All these tools are very easy to use, and very easy to <em>misuse</em>. Be careful.</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"summary\">Summary<a href=\"#summary\" class=\"hash-link\" aria-label=\"Direct link to Summary\" title=\"Direct link to Summary\">​</a></h2><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"mindset-tips\">Mindset Tips<a href=\"#mindset-tips\" class=\"hash-link\" aria-label=\"Direct link to Mindset Tips\" title=\"Direct link to Mindset Tips\">​</a></h3><p>Don't get so caught up in planning on buying specific tools or brands unless you <em>really</em> know what you want.</p><p>Otherwise you'll spend hours and hours on woodworking forums, guitar forums, electronics forums, and YouTube <em>thinking</em> and <em>spectating</em> instead of <em>doing</em>.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"hands-on-tips\">Hands-On Tips<a href=\"#hands-on-tips\" class=\"hash-link\" aria-label=\"Direct link to Hands-On Tips\" title=\"Direct link to Hands-On Tips\">​</a></h3><ul><li><p>Measure twice, cut once.</p></li><li><p>When you're cutting out the body, don't try to trace the line exactly with the jigsaw. Drill holes near curves, and aim at the holes with the jigsaw. (You could even do this with a handsaw, save yourself some money!) Make the shape nicer using a file &amp; rough sandpaper.</p></li><li><p>If you don't have a drill watch the video below titled 'Relief Cuts'.</p></li><li><p>If you don't have a soldering iron and solder laying around, buy a pre-wired harness from guitarfetish(GFS). Also, you can make good joints using electrical tape and zip ties, see the video linked below 'Joining Wires with NO Solder'</p></li></ul><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"helpful-videos-i-watched\">Helpful Videos I Watched<a href=\"#helpful-videos-i-watched\" class=\"hash-link\" aria-label=\"Direct link to Helpful Videos I Watched\" title=\"Direct link to Helpful Videos I Watched\">​</a></h2><p>Joining Wires with NO Solder<br>\n<a href=\"https://www.youtube.com/watch?v=f0Gierk4SCw\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.youtube.com/watch?v=f0Gierk4SCw</a><br>\nTLDR: Use Electrical tape to join the wires, zip tie around the electrical tape.</p><p>Relief Cuts<br>\n<a href=\"https://www.youtube.com/watch?v=5qCY-iXPvw8\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.youtube.com/watch?v=5qCY-iXPvw8</a></p><p><em>What Can You Do With A Jigsaw?</em><br>\n<a href=\"https://www.youtube.com/watch?v=jWneDzqAbH8\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.youtube.com/watch?v=jWneDzqAbH8</a><br>\nSmoother cuts on the side of the board the teeth are immediately facing (upward pointing blades make smoother lines on underside of board)</p><p>Cutting A Guitar Body Out With Hand Tools (2X speed this guy speaks slow)<br>\n<a href=\"https://www.youtube.com/watch?v=2Lm3l_GOAOQ\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.youtube.com/watch?v=2Lm3l_GOAOQ</a></p><p>Reducing Hum<br>\n<a href=\"https://www.youtube.com/watch?v=FI_v-NjBRKc\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.youtube.com/watch?v=FI_v-NjBRKc</a><br></p><ul><li>Use foil, but not too much.</li><li>Ground everything to one place (Star Grounding)</li><li>Twist the pickup's power and ground wires together for phase cancellation and length reduction</li></ul><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"schematics--plans\">Schematics / Plans<a href=\"#schematics--plans\" class=\"hash-link\" aria-label=\"Direct link to Schematics / Plans\" title=\"Direct link to Schematics / Plans\">​</a></h2><p>[My custom plan]<!-- -->(/downloads/luis paul jr schemes 4 WEB.zip)</p><p>I adapted my plan from here: <a href=\"https://www.electricherald.com/guitar-templates/\" target=\"_blank\" rel=\"noopener noreferrer\">Electric Herald High Quality Guitar Templates</a></p>",
            "url": "https://whoislewys.com/blog/2018/06/25/Building My Own Guitar",
            "title": "How I Built My Own Guitar",
            "summary": "With hand tools and NO woodworking experience",
            "date_modified": "2018-05-23T12:00:00.000Z",
            "author": {
                "name": "Luis Gomez",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "DIY",
                "Music",
                "Audio"
            ]
        },
        {
            "id": "https://whoislewys.com/blog/learn-audio-compression",
            "content_html": "<h5 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"index\">Index<a href=\"#index\" class=\"hash-link\" aria-label=\"Direct link to Index\" title=\"Direct link to Index\">​</a></h5><p>Here are quick links to each section. <strong>If you don't know where to start, go to Downloads.</strong></p><p><a href=\"#downloads---start-here\">Downloads</a><br>\n<a href=\"#video\">Video</a><br>\n<a href=\"#transcription\">Transcription</a><br>\n<a href=\"#compressor-controls\">Compressor Controls</a><br>\n<a href=\"#threshold\">Threshold</a><br>\n<a href=\"#ratio\">Ratio</a><br>\n<a href=\"#attack\">Attack</a><br>\n<a href=\"#release\">Release</a><br>\n<a href=\"#settings\">Example Settings</a><br>\n<a href=\"#fin\">FIN (comment section)</a></p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"downloads---start-here\">Downloads - Start Here<a href=\"#downloads---start-here\" class=\"hash-link\" aria-label=\"Direct link to Downloads - Start Here\" title=\"Direct link to Downloads - Start Here\">​</a></h2><p>Download the <strong>FREE</strong> <a download=\"\" target=\"_blank\" href=\"/downloads/compression-tut/Compression Cheatsheet.pdf\">Compressor Cheatsheet</a> for an easy, one-page summary of compressor fundamentals.</p><blockquote><p>Note: the PDF above is A4 sized &amp; optimized for web. <br>\nFor a <em>great</em> looking printout and files good for print, please go to the shop.</p></blockquote><img loading=\"lazy\" src=\"/downloads/compression-tut/Compression Cheatsheet.png\" class=\"img_ev3q\"><p>Print &amp; plaster it on your wall, gift it to your producer, make it your phone background, whatever.</p><p>If you want to go deeper and really <em>get a feel</em> for each compressor control, then download this drum loop and free compressor to follow along with the video below!</p><ul><li><p><a download=\"\" target=\"_blank\" href=\"/downloads/compression-tut/compression-tut-drum-loop.wav\">Drum Loop </a>(Right click ‚ --&gt; Save Link As)</p></li><li><a href=\"https://www.reaper.fm/reaplugs/\" target=\"_blank\" rel=\"noopener noreferrer\">Free ReaComp Compressor VST</a> (Have to download entire pack, but it's a small download)</li></ul><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"video\">Video<a href=\"#video\" class=\"hash-link\" aria-label=\"Direct link to Video\" title=\"Direct link to Video\">​</a></h2><p>Video embed will go here</p><h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"transcription\">Transcription<a href=\"#transcription\" class=\"hash-link\" aria-label=\"Direct link to Transcription\" title=\"Direct link to Transcription\">​</a></h2><p>Welcome to this video, you are very brave. I'm going to help you get a feel for how a compressor works without making it complicated. You're intuitively going to know what the controls do.</p><p>I'm also gonna link you to one of the best sounding free compressors and give you a couple example settings for adding punch and thickening your tracks.</p><p>Let's do it.</p><p>First we'll cover the fundamentals of compression:</p><ul><li>what compression does</li><li>common compressor controls &amp; what they mean</li></ul><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-is-compression\">What is compression?<a href=\"#what-is-compression\" class=\"hash-link\" aria-label=\"Direct link to What is compression?\" title=\"Direct link to What is compression?\">​</a></h3><p> It changes the shape of audio signals by reducing volume. This 'reducing volume' is also called gain reduction. Same thing. The two most common uses of a compressor are to make audio punchier, and to make audio 'thicker' or more even.</p><ol><li><p>So say we want to make our drums punchier. How do we do this by reducing volume? We leave the volume alone for the initial drum hit, then we reduce the volume right after. Then bring the volume up just in time for the next drum hit. This gives you a punchier sound.</p></li><li><p>We can also make the drums sound more even, or thicker or fatter if you like. For this, we turn down the volume for the initial punch/ poke of each drum hit and turn the volume back up to normal quickly. This will smooth out the initial hit of the drums, leaving you with a more even signal with more body. The drum tone stays present or sustains for a longer time</p></li><li><p>So, how do we make a compressor do this for us?</p></li></ol><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"compressor-controls\">COMPRESSOR CONTROLS<a href=\"#compressor-controls\" class=\"hash-link\" aria-label=\"Direct link to COMPRESSOR CONTROLS\" title=\"Direct link to COMPRESSOR CONTROLS\">​</a></h3><p>To understand compression, we just need 4 simple controls. I recommend you follow along, there are FREE download links for the drum loop and compressor in the description and also a handy and beautiful Compression Cheatsheet with all this info on it, but if you just wanna watch that's cool too we're gonna learn regardless.</p><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"threshold\">Threshold<a href=\"#threshold\" class=\"hash-link\" aria-label=\"Direct link to Threshold\" title=\"Direct link to Threshold\">​</a></h4><ul><li>When input audio crosses this level, the compressor can begin reducing audio</li><li>Ex: I set the threshold to -18db. When the audio reaches -18db or higher, the compressor knows it can start lowering the volume.</li></ul><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"ratio\">Ratio<a href=\"#ratio\" class=\"hash-link\" aria-label=\"Direct link to Ratio\" title=\"Direct link to Ratio\">​</a></h4><ul><li>The strength of the compressor's volume reduction</li><li>Ex. 1:1 ratio - 0 strength. Doesn't do a thing. 3:1 ratio, audio gets pulled down a bit. 10:1 audio gets pulled down strongly when it goes above the threshold.</li><li>Ratio numbers:    Between <!-- -->[1:1 - 4:1]<!-- --> is light ratio, somewhere around <!-- -->[4:1 - 8:1]<!-- --> is medium ratio,  <!-- -->[8:1 - 20:1]<!-- --> is pretty high ratio</li></ul><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"attack\">Attack<a href=\"#attack\" class=\"hash-link\" aria-label=\"Direct link to Attack\" title=\"Direct link to Attack\">​</a></h4><ul><li>How quickly the compressor starts working</li><li>Ex. 50ms attack time. When the audio crosses the threshold, the compressor waits 50ms, then reduces the volume of the audio.</li><li>Intuitively, Faster attack times will push audio peaks down, slower attack times will push tails down.</li><li>Attack Numbers: fast attack time is <!-- -->[0ms - 15ms]<!-- -->, medium attack time <!-- -->[15ms - 50ms]<!-- -->, slow <!-- -->[50ms - 200ms]</li></ul><h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"release\">Release<a href=\"#release\" class=\"hash-link\" aria-label=\"Direct link to Release\" title=\"Direct link to Release\">​</a></h4><ul><li>Tells the compressor when to let go of its compression.</li><li>It controls how fast the audio recovers from the gain reduction.  It controls how fast the audio bounces back from the gain reduction.</li><li>Fast release times let your audio 'recover' from compression faster. Slower release times keep your audio 'held down' for longer, or keep your audio squashed for longer.</li><li>Fast release time <!-- -->[0ms - 15ms]<!-- -->, medium attack time <!-- -->[15ms - 50ms]<!-- -->, slow<!-- -->[50ms - 200ms]</li><li>Full Example: Choose a release time of 100ms. Audio crosses threshold, compressor waits 50ms of attack time, compressor reduces volume, the volume reduction is held down for 100 ms, then volume goes back to normal.</li></ul><p>That's all the controls. Not that scary right?\nLet's apply our knowledge and wrap this little lecture up with some example settings for punch and settings for thickness / even-ness</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"settings\">Settings!!!<a href=\"#settings\" class=\"hash-link\" aria-label=\"Direct link to Settings!!!\" title=\"Direct link to Settings!!!\">​</a></h3><p>Alright so the theory is great. But what numbers should I look for?</p><p>If I want to make my vocals sound thicker and more 'in your face,'</p><p>If I want to make bone crushing drum sounds how do I do that?</p><p>Real quick, it all depends on the track. So DO NOT just copy my settings here. Use them as a starting point. The only way to master compression is learn what the controls do and play around.</p><p>Also, make sure you like the balance of all your tracks. If the volumes of your tracks out of a wack, compression will not help! Get a balance you like just by adjusting the faders.</p><p>Alright that said, let's get to the settings.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"add-punch-medium-attack--medium--slow-release\">Add Punch: Medium Attack &amp; Medium &amp; Slow release<a href=\"#add-punch-medium-attack--medium--slow-release\" class=\"hash-link\" aria-label=\"Direct link to Add Punch: Medium Attack &amp; Medium &amp; Slow release\" title=\"Direct link to Add Punch: Medium Attack &amp; Medium &amp; Slow release\">​</a></h3><p>Lots of people talk about using compressors to add punch. Here are some typical settings and what it sounds like.</p><ul><li><p>We're gonna look for 10db of gain reduction on our meter right here.</p></li><li><p>We're gonna use an 8:1 ratio to really clamp down on the drum sound after the initial hit. We've got an attack time of 20 to reduce the tail right after each hit, and release of 150 so the volume recovers a bit in between hits.</p></li><li><p>Finally, threshold depends on the volume of your input audio so I'll just set to wherever I start seeing around 10db of gain reduction.</p></li></ul><p>This probably isn't something you'd want to send out, but I'm making the effect very dramatic to demonstrate what 'punch' sounds like from a compressor.</p><p>Hear how the medium attack time lets the very beginning of each drum hit through? If I reduce this the drums get clickier and clickier. If I slow down the attack, we start getting this weird sound. But right about 15-40 we get these nice beefy drum hits with little tail. You might wanna set this smaller for a faster tempo song, or larger for a slower tempo song.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"thickness--even-ness-fast-attack--fast-release\">Thickness / Even-ness: Fast Attack &amp; Fast Release<a href=\"#thickness--even-ness-fast-attack--fast-release\" class=\"hash-link\" aria-label=\"Direct link to Thickness / Even-ness: Fast Attack &amp; Fast Release\" title=\"Direct link to Thickness / Even-ness: Fast Attack &amp; Fast Release\">​</a></h3><ul><li><p>We're gonna look for around 5db gain reduction.</p></li><li><p>Ratio we're going 4:1, attack 0.3ms, release 20ms.</p></li><li><p>Again, threshold depends on your input volume.</p></li></ul><p>Hear how these really fast attack and release times will squash the initial hits of the drums and leave them with a lot of sustain</p><p>This makes the drums less pokey, more controlled and fatter. This also allows you to bring the volume up louder than you could have before compression.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"one-last-note\">One Last Note<a href=\"#one-last-note\" class=\"hash-link\" aria-label=\"Direct link to One Last Note\" title=\"Direct link to One Last Note\">​</a></h3><p>One last note, It's human nature for us to think louder is better.</p><p>So when you're trying out different compressor settings be careful to not just toss a compressor in just because it makes your audio louder. A lot of presets do that.</p><p>Check meters in your DAW, listen carefully, and toggle the compressor on and off to make sure the volume is THE SAME before and after compressing.</p><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"fin\">FIN<a href=\"#fin\" class=\"hash-link\" aria-label=\"Direct link to FIN\" title=\"Direct link to FIN\">​</a></h3><p>Congratulations! That's all the fundamentals of compression, with some solid settings to get you started. Have fun with it</p><p>There is one common setting we didn't discuss cause we need to understand the others first: knee.</p><p>It can make compression sound more natural. If you want to know how it works, There's a link in the description to an Ableton Forum thread that describes it beautifully and simply - it's also described briefly in the Compression Cheatsheet.</p><p>Leave a like if you liked, if you have a question or a request or you think I suck leave a comment, if you'd like more subscribe to the channel. That's it Peace! 😊</p><ul><li>Luis 2018.05</li></ul>",
            "url": "https://whoislewys.com/blog/learn-audio-compression",
            "title": "Learn Audio Compression - Without A Ph.D. (+Cheatsheet)",
            "summary": "Index",
            "date_modified": "2018-05-23T00:00:00.000Z",
            "author": {
                "name": "Luis Gomez",
                "url": "https://github.com/whoislewys"
            },
            "tags": [
                "audio",
                "compression"
            ]
        }
    ]
}