<?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, 07 Mar 2026 04:40:02 +0000</lastBuildDate>
    
    <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>
      
      <category>engineering</category>
      
    </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>
    
    <item>
      <title>No service, still broken</title>
      <link>/posts/no-service-still-broken/</link>
      <pubDate>Sun, 27 Jun 2021 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/no-service-still-broken/</guid>
      <description>The Boundary Waters Canoe Area Wilderness is a million acres of lakes, forest, and wetlands along the Minnesota-Canada border. No motors allowed. No roads. No cell towers. You paddle in, portage your gear between lakes, and camp on designated sites. It’s one of the most heavily protected wilderness areas in the country, and it’s been under constant threat from copper-nickel mining proposals for decades. The sulfide ore deposits sitting beneath the watershed would be worth billions, and every few years someone makes another run at extracting them. So far the wilderness has held. For now. I go there when I...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>How to create a Ruby HTTP API client gem</title>
      <link>/posts/create-a-ruby-http-api-client-gem/</link>
      <pubDate>Wed, 05 May 2021 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/create-a-ruby-http-api-client-gem/</guid>
      <description>Most API client gems are either too clever or not clever enough. You either get a DSL that fights you at every turn, or a thin wrapper that barely saves you from writing raw HTTP calls. This post walks through building one from scratch—the kind that feels obvious once it exists but requires real decisions along the way. Why not use an existing solution? Fair question. RestClient and ActiveResource exist. So does just using Net::HTTP directly. Here’s the thing: generic solutions optimize for generality. They can’t make assumptions about your API’s conventions, error formats, or authentication scheme. You end up...</description>
      
      <category>ruby</category>
      
    </item>
    
    <item>
      <title>Invest in your seeds file</title>
      <link>/posts/invest-in-your-seeds-file/</link>
      <pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/invest-in-your-seeds-file/</guid>
      <description>Every Rails project I inherit has the same seeds file: three admin users, two enum reference tables, and a commented-out block that “worked at some point.” The file hasn’t been run successfully in months. Nobody knows what state it expects or what state it produces. Meanwhile, the development database is a graveyard of test records, half-finished features, and that one user account everyone shares because nobody remembers the password to the others. When something breaks, you spend twenty minutes trying to reproduce it with the right combination of data. When a new developer joins, they spend a day getting their...</description>
      
      <category>ruby</category>
      
    </item>
    
    <item>
      <title>Writing as conversation</title>
      <link>/posts/writing-as-conversation/</link>
      <pubDate>Wed, 16 Sep 2020 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/writing-as-conversation/</guid>
      <description>People ask how I developed my writing voice. I don’t have a good answer. I didn’t study craft. I didn’t read books about writing. I just wrote a lot, and eventually it started sounding like me. The closest I can get to explaining it: I’m not writing to anyone. I’m having a conversation with the screen. The medium as the other person When I sit down to write, I’m not thinking about readers. I’m not thinking about audience. I’m talking to the thing in front of me—the text field, the markdown file, whatever. It shows me what I just said,...</description>
      
      <category>personal</category>
      
    </item>
    
    <item>
      <title>Ruby blocks in a few minutes</title>
      <link>/posts/ruby-blocks-in-a-few-minutes/</link>
      <pubDate>Sun, 19 Jul 2020 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/ruby-blocks-in-a-few-minutes/</guid>
      <description>Here’s the thing about blocks in Ruby: they’re just chunks of code you pass around. The yield keyword is how you run them. That’s it. Everything else is details. Let’s start with a standard method definition: def hello_world! puts &quot;hello world!!!!!&quot; &quot;🌎&quot; end hello_world! Run Reset loads ~35MB Ruby environment Error: invalid byte sequence in US-ASCII This looks like this under the hood: define_singleton_method :hello_world! do puts &quot;hello world!!!!!&quot; &quot;🌎&quot; end Run Reset loads ~35MB Ruby environment Error: invalid byte sequence in US-ASCII Let’s write our own fancy define_method method. Note that because we’re in IRB, we need to define...</description>
      
      <category>ruby</category>
      
    </item>
    
    <item>
      <title>I learned HTML from chickens</title>
      <link>/posts/i-learned-html-from-funky-chickens/</link>
      <pubDate>Wed, 19 Feb 2020 00:00:00 +0000</pubDate>
      <guid isPermaLink="true">/posts/i-learned-html-from-funky-chickens/</guid>
      <description>I learned HTML from a website called Funky Chickens. This was 1999. The site promised to teach you “the special codes that make your website colorful, cool, and professional looking.” It had a page dedicated to explaining what the &amp;lt;blink&amp;gt; tag did. I thought this was the height of web development. I was eight. My pages lived on freewebs.com, expages.com, geocities.com, angelfire.com, and tripod.com—whichever free host hadn’t yet suspended me for exceeding my 5MB storage limit with GIFs. I was active on buddy4u. The pages were objectively terrible. Tiled backgrounds that induced migraines. Visitor counters that I refreshed myself to...</description>
      
      <category>personal</category>
      
    </item>
    
  </channel>
</rss>
