<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Josh Brody</title>
    <description>Federal prison alumnus, product engineer,  and writer when I have something to say. These are all related.</description>
    <link>/</link>
    <atom:link href="/feed.xml" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Sat, 30 May 2026 18:05:45 +0000</lastBuildDate>
    
    <item>
      <title>No more asking &apos;who are you&apos;; we have all been reduced to a passphrase.</title>
      <link>/posts/we-have-all-been-reduced-to-a-passphrase/</link>
      <pubDate>Sat, 30 May 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/we-have-all-been-reduced-to-a-passphrase/</guid>
      <description>What Passphrase Are You? I went to prison. I don’t hide this. The shame has passed. While I was gone, someone stole everything I owned in the physical world and then, for good measure, changed the passwords on most of my digital life. This included my GitHub account, moved my domain name across three registrars, my Google Workspace account tied to that domain name (as had been for 13 years), 2FA keys—you know, just about everything. I’ve gotten most of my digital accounts back—I’ve lawyered up for the personal stuff (some of which has been posted on Facebook Marketplace). I...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>The moat was never the code</title>
      <link>/posts/the-moat-was-never-the-code/</link>
      <pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/the-moat-was-never-the-code/</guid>
      <description>The moat was never the code I ran an unauthorized sports streaming service from 2016 to 2021. It was called HeheStreams. I charged $125 a year; users could have a single annual payment of $100. My infrastructure cost was $75 a month. HeheStreams didn’t re-encode or re-broadcast streams. My users consumed them directly from licensed platforms—using the same CDN and DRM as the platform’s paying users. I implemented more than a dozen different platforms in the name of feature parity across each sport. People who know the technical side assume that was the hard part. It was hard. It was...</description>
      
      <category>startups</category>
      
    </item>
    
    <item>
      <title>statement_timeout 0: Dirty plates, dead cooks, and kitchen fires</title>
      <link>/posts/statement-timeout-0-casually/</link>
      <pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/statement-timeout-0-casually/</guid>
      <description>so you got tired of alerts that your queries were timing out. that’s bad because alerts are good even though nobody likes the alerts. my mom didn’t like acknowledging she might have cancer either so she just ignored the headaches and vertigo and balance issues and then it was like ope stage IV brain cancer. the name makes it sound like a fix. it’s not. removing the timeout doesn’t make the slow queries faster, it just lets them run forever, and “forever” on a 1tb db with bad queries is how you lose the restaurant. let me explain what i...</description>
      
      <category>engineering</category>
      
    </item>
    
    <item>
      <title>We made this hard: over-engineering the web</title>
      <link>/posts/we-made-this-hard/</link>
      <pubDate>Tue, 28 Apr 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/we-made-this-hard/</guid>
      <description>We made this hard The job hasn’t changed in thirty years. A request comes in. You return some HTML. The browser draws it. Maybe you sprinkle some CSS on top so it doesn’t look like a 1996 GeoCities fever dream, or maybe you do so it does. That’s the whole gig. Somewhere along the way we convinced ourselves this hard. It isn’t. It really, really isn’t. And the people paying for our confusion are the ones who can least afford it: the restaurant owner who just wants a menu online, the babysitter trying to put up a contact form, and—maybe...</description>
      
      <category>engineering</category>
      
    </item>
    
    <item>
      <title>How Postgres works at scale</title>
      <link>/posts/how-postgres-works-at-scale/</link>
      <pubDate>Sat, 11 Apr 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/how-postgres-works-at-scale/</guid>
      <description>A single idle-in-transaction connection can starve every table in your database. Most senior Rails devs don’t know that. You learned MVCC the way most of us did—”writers don’t block readers,” concurrent transactions get isolated snapshots, UPDATE doesn’t lock the table. That’s marketing copy. The operational reality is that those snapshots have a cost, and the cost compounds across every table at once if one connection gets stuck. I’m going to try to translate what this mental model is that I’ve built while building and operating a Rails-based data pipeline that handles 10TB/day. Not “what is MVCC”—you’ve heard that—but instead consequences...</description>
      
      <category>postgres</category>
      
    </item>
    
    <item>
      <title>Your README is a landing page, you just don&apos;t know it yet</title>
      <link>/posts/landing-pages-for-open-source-maintainers/</link>
      <pubDate>Tue, 24 Mar 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/landing-pages-for-open-source-maintainers/</guid>
      <description>I have a few open-source libraries that you could call popular. I use a lot of others. Open source isn’t a README, but it’s the cheapest marketing a developer has. A world-class marketing person opens your README the same way they’d open a landing page, because that’s what it is. If you just wrote it like internal documentation—like developers wane through all day—it’s going to become a sore. The marketing-brain version treats the README as a conversion funnel where the conversion is “developer tries this and tells a friend.” You spent six months on the library and four minutes on...</description>
      
      <category>open-source</category>
      
      <category>writing</category>
      
    </item>
    
    <item>
      <title>Every company has two org charts. One of them is bullshit</title>
      <link>/posts/every-company-has-two-org-charts-and-one-is-bullshit/</link>
      <pubDate>Tue, 03 Mar 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/every-company-has-two-org-charts-and-one-is-bullshit/</guid>
      <description>Every company has two org charts. The first one lives in the HR system. It’s clean. It shows who reports to whom, who has budget authority, who’s in charge of what. It’s useful for figuring out who to cc on an email and who approves your time off. Beyond that, it’s mostly decorative. The second one doesn’t exist anywhere you can see it. No document, no diagram, no Confluence page. It’s the one that actually determines what happens—who gets consulted before a decision gets made, whose opinion the VP asks for before committing to a technical direction, who can block...</description>
      
    </item>
    
    <item>
      <title>Wide tables and query validation</title>
      <link>/posts/wide-tables-and-query-validation/</link>
      <pubDate>Sat, 03 Jan 2026 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/wide-tables-and-query-validation/</guid>
      <description>I’m building an ETL system that imports billions of person records from data vendors. Each vendor sends different attributes—household income, automotive interests, education level, charitable donor status—in their own formats with their own column names. The system normalizes everything into a canonical schema and exposes a query interface for building audiences. The obvious choice for storing hundreds of optional attributes is JSONB or EAV. Flexible, no migrations needed, handles sparse data well. I went with wide tables instead: every queryable attribute is a column on the people table. This sounds like a 2005 decision. But Postgres handles wide tables fine—you...</description>
      
      <category>ruby</category>
      
    </item>
    
    <item>
      <title>I got out of federal prison and couldn&apos;t log into my GitHub</title>
      <link>/posts/i-got-out-of-federal-prison-and-couldnt-log-into-my-github/</link>
      <pubDate>Sun, 28 Dec 2025 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/i-got-out-of-federal-prison-and-couldnt-log-into-my-github/</guid>
      <description>I got out of prison and couldn’t log into my GitHub. The password while I was gone. The phone number tied to the account got transferred to another carrier. The device with my two-factor authentication codes—gone. Eighteen months away, and I came back to find myself locked out of my own account. GitHub, for those unfamiliar, is where programmers store and share code. It’s the largest platform of its kind. Your GitHub account is your professional identity if you work in software. It’s your portfolio, your history, your proof of work. Mine had twelve years of contributions on it. Every...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>Interactive Rails playgrounds</title>
      <link>/posts/interactive-rails-playgrounds/</link>
      <pubDate>Mon, 01 Dec 2025 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/interactive-rails-playgrounds/</guid>
      <description>I built a WASM-compatible Rails playground that runs entirely in your browser. No server, no setup, just Rails. This is built on the backs of people much smarter than me. Try it out below. The VM takes a few seconds to load, but once it’s ready you can run any Active Record code against a real SQLite database. -- examples -- Post.published.map(&amp;amp;:title) This does not play nicely with iOS. Run Reset loads ~76MB Rails environment Post Load (0.1ms) SELECT &quot;posts&quot;.* FROM &quot;posts&quot; WHERE &quot;posts&quot;.&quot;published&quot; = 1 =&amp;gt; [&quot;Getting Started with Ruby&quot;, &quot;Rails is Magic&quot;] The stack The playground runs wasmify-rails,...</description>
      
      <category>ruby</category>
      
    </item>
    
    <item>
      <title>How to get a job as a felon</title>
      <link>/posts/how-to-get-a-job-as-a-felon/</link>
      <pubDate>Thu, 20 Nov 2025 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/how-to-get-a-job-as-a-felon/</guid>
      <description>Hi, I’m Josh. I was inmate number 71690-509. I went to federal prison. I got a job after. Here’s what I observed. This isn’t motivational. I’m not going to tell you to “stay positive” or that “everything happens for a reason.” If you’re reading this, you’ve probably already waded through that garbage. You want to know what actually works. Fair warning: my crime was running a sports streaming website. That’s a “cool” crime in comparison to many. People hear it and go “oh, what?!” rather than recoiling. If you’re reading this with an assault conviction or something involving theft, your...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>When you don&apos;t look autistic</title>
      <link>/posts/when-you-dont-look-autistic/</link>
      <pubDate>Tue, 14 Oct 2025 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/when-you-dont-look-autistic/</guid>
      <description>I get this a lot. “You don’t look autistic.” It’s meant as a compliment, I think. Like I’ve done a good job of hiding it. Gold star for passing. Or it’s a remark that implies I don’t have autism because I don’t visibly struggle. You know, like you can’t have COVID if you don’t get tested. Or you don’t look like you have cancer—is that one? What I want to ask—but don’t, because that would be rude and I’ve learned not to be rude in ways that upset people—is: what does autistic look like? What they expect Based on the...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>Going to federal prison for piracy</title>
      <link>/posts/going-to-federal-prison-for-piracy/</link>
      <pubDate>Thu, 18 Sep 2025 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/going-to-federal-prison-for-piracy/</guid>
      <description>Here’s something I’ve been putting off writing: I spent eighteen months in federal prison. The charge was computer fraud. The context is weirder. In 2016, I built a website called HeheStreams. It started as a joke—a proof of concept I posted on Reddit after BallStreams, my beloved NBA streaming site, vanished overnight. Nobody picked it up, so I kept working on it. Eventually I slapped a paywall on it, thinking it was ludicrous that anyone would pay for such a thing. People paid for such a thing. What HeheStreams actually was HeheStreams let users watch live sports from MLB, NBA,...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>Loud for selfish reasons</title>
      <link>/posts/loud-for-selfish-reasons/</link>
      <pubDate>Mon, 01 Sep 2025 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/loud-for-selfish-reasons/</guid>
      <description>I’m pretty open about being autistic. I write about it. I mention it at work. I don’t hide it when it comes up in conversation. People sometimes tell me this is brave, which is weird, because bravery implies I’m doing something hard for noble reasons. I’m not. I’m doing it because it’s easier than the alternative. How I got here I wasn’t always like this. For most of my life I hid it—or more accurately, I didn’t know what I was hiding. I just knew something was off and I didn’t want people to see it. The shift happened in...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>HTTP 451</title>
      <link>/451/</link>
      <pubDate>Tue, 31 Dec 2024 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/451/</guid>
      <description>SEALED Case No. 22-cr-00350 (S.D.N.Y.) 451 Unavailable For Legal Reasons The content you are attempting to access has been restricted due to a legal demand. FCI Thomson Inspected Hey. So I&apos;m in federal prison now. Long story. Actually it&apos;s not that long, it&apos;s just ██████████. Shit happens. Anyway. I&apos;ve been reading a lot. The library here has exactly one book I care about: HTML for the World Wide Web from 1999. There&apos;s a chapter on &quot;publishing your web page to GeoCities.&quot; I&apos;ve read it four times. Not because it&apos;s useful. Because it&apos;s the closest thing to a religious experience I...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>Gem configuration patterns</title>
      <link>/posts/gem-configuration-patterns/</link>
      <pubDate>Sat, 12 Aug 2023 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/gem-configuration-patterns/</guid>
      <description>Configuration is often the first thing users interact with in your gem. It’s also the part most gem authors spend the least time thinking about—which explains a lot. This post covers the patterns you’ll encounter and implement, ordered from the simplest to the most involved. Module accessors The simplest possible approach. You expose a few attributes on your gem’s main module and call it a day. module MyGem class &amp;lt;&amp;lt; self attr_accessor :api_key, :timeout, :debug end self.timeout = 30 self.debug = false end MyGem.api_key = &quot;sk-123&quot; MyGem.timeout = 60 puts &quot;api_key: #{MyGem.api_key}&quot; puts &quot;timeout: #{MyGem.timeout}&quot; puts &quot;debug: #{MyGem.debug}&quot; Run Reset...</description>
      
      <category>ruby</category>
      
    </item>
    
    <item>
      <title>Creating a conventional, Stripe-like API with Grape and Ruby on Rails</title>
      <link>/posts/conventional-api-with-grape-and-ruby-on-rails/</link>
      <pubDate>Fri, 24 Mar 2023 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/conventional-api-with-grape-and-ruby-on-rails/</guid>
      <description>Here’s the thing about API endpoints: you end up writing the same boilerplate over and over. Pagination metadata. Object type annotations. Wrapping collections in data arrays. Every endpoint looks identical except for the one line that matters—the actual query. I got tired of it. So I built a convention system with Grape that handles all of it automatically. Now my endpoint code looks like this: # thing.rb get do pagy(Author.all) end And the response looks like this: { &quot;data&quot;: [ { &quot;id&quot;: 1, &quot;name&quot;: &quot;joshmn&quot;, &quot;object&quot;: &quot;author&quot; } ], &quot;has_more&quot;: true, &quot;object&quot;: &quot;list&quot; } No manual JSON construction. No remembering...</description>
      
      <category>ruby</category>
      
    </item>
    
    <item>
      <title>A plea for administration</title>
      <link>/posts/a-plea-for-administration/</link>
      <pubDate>Tue, 27 Dec 2022 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/a-plea-for-administration/</guid>
      <description>Here’s the thing about admin panels: nobody wants to build them, nobody wants to maintain them, and the people who need them most are the ones with the least power to demand them. This is a problem. I ran a platform once. Not a big one, but big enough to have customer service people who weren’t me. From day one, I gave them tools—real tools. They could reset passwords, grant trials, see debugging info, approve orders. They could actually solve problems without filing a ticket and waiting for me to wake up. Was this risky? Maybe. But I worked with...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>Database constraints first, validations second</title>
      <link>/posts/database-constraints-first-validations-second/</link>
      <pubDate>Thu, 24 Nov 2022 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/database-constraints-first-validations-second/</guid>
      <description>A few years ago I inherited a codebase where the users table had no unique constraint on email. The model had validates :email, uniqueness: true, so everything seemed fine—until I found 847 duplicate email addresses in production. Someone had written a rake task. It bypassed ActiveRecord. The model validation never ran, and the database didn’t care. Nearly a thousand users couldn’t log in because the system kept finding the wrong account. This is the kind of bug that makes you question your career choices. The conventional approach Rails tutorials teach you to think about validation first. You write validates :email,...</description>
      
      <category>ruby</category>
      
    </item>
    
    <item>
      <title>REST-only controllers</title>
      <link>/posts/rest-only-controllers/</link>
      <pubDate>Sat, 11 Jun 2022 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/rest-only-controllers/</guid>
      <description>A few months into a project, I watched a developer add a mark_complete action to a TasksController that already had archive, unarchive, assign, unassign, prioritize, move_up, move_down, and duplicate. The routes file had so many member blocks it looked like a DSL for avoiding REST. Each custom action did one thing, took maybe five lines of code, and was perfectly reasonable on its own. But collectively they’d turned the controller into a junk drawer. The before_action filters had grown into a decision tree. The tests were full of post :archive, params: { id: task.id } mixed with patch :update, params:...</description>
      
      <category>ruby</category>
      
    </item>
    
  </channel>
</rss>
