
Principal Whimsy Engineer for myself, Staff Software Engineer for @GustoHQ. Hobby hobbyist, husband, father, noun, https://xitter.gq/t.co/9L8pwSaRrY . Opinions are my own.
Atlanta, GA
Joined June 2007
- Tweets12.6K
- Following2.7K
- Followers2.9K
- Likes12.3K
Josh Nichols retweeted
Everyone needs a secure and scalable agent plane, regardless of if you’re fully factory pilled or just running Claude code on a terminal TTY. I’m making my own because I think everything available isn’t paranoid enough.
Every company should own their secure cloud dev environment where agents can change code, run the full application, and verify their work. At @depthfirstlabs we care tremendously about this and today we share how we built our environment.
Each developer gets a persistent devbox, and each task gets its own application stack, database, and workflow state, so agents work in parallel. Engineers have flexibility to switch between Codex, Claude Code, and other agents without losing context.
The environment runs inside our private AWS network, with access controlled through @Tailscale and package requests checked by depthfirst’s Dependency Firewall in configured build and registry paths.
When an agent finishes an iteration, it can share reproduction steps, video evidence, and a private preview URL. We can open the exact environment it tested and review the behavior ourselves.
We had it operational within a week.
Josh Nichols retweeted
Your factory should be accessible everywhere your team works. This is key for adoption across a team. This means multiplayer prompting in Slack, tagging from task boards like Linear or Jira, or fully automating against a backlog. It should also be accessible via MCP to put work in through local agents, and via API.
You’ll find over time that it's convenient to hop between all of these mediums throughout the lifecycle of a task.
Josh Nichols retweeted
Most companies didn't go remote — they digitized the office. Same meetings, same interruptions, now on Zoom.
Open and Async is out today: the open-source way of working — in the open, in writing, async by default — turned into a playbook for your team.
open-and-async.com
PSA: if you need the exact text off a page in Claude Code, don't WebFetch it. curl it.
WebFetch laundres the page through a small summarizer model. You get a paraphrase, not the bytes. Fine for the gist, wrong for exact docs/API signatures/llms.txt.
bit.ly/3R3DPN0
Josh Nichols retweeted
Situation 1: dev A thinks approach X is correct, dev B thinks Y is the right way. They argue and try to convince each other.
Situation 2: dev A thinks approach X is correct, tells the LLM to implement it.
There is SO MUCH learning in Situation 1, lost when using LLMs....
Claude Code prompts for `python3 -c '# comment\nprint(42)'` even with auto-mode on. Not a bug.
There's a Bash parser in there with opinions, one of 6 layers between your keystroke and execution. Mapped them all.
pickles.dev/claude-codes-per…
Repeat after me: CLAUDE.md, skills, and anything else in the context window is at best a suggestion. Hooks are the only way to enforce behavior
two weeks running Claude Code with /sandbox. here's the config that actually works, the stuff that broke, and the stuff that's still broken.
pickles.dev/the-sandbox-conf…
"That's awesome, congrats" takes thirty seconds. "Great, now what about Y?" takes the same thirty seconds but leaves someone deflated.
Wrote about the skipped acknowledgment and why we keep doing it to each other
pickles.dev/can-we-just-sit-…
~/.claude/ is basically production. No staging, no isolation. Every Claude Code plugin mutates your daily driver config. Wrote up how to set up a proper test environment after borking mine one too many times. bit.ly/4r7VlvC
My team is hiring! Come work on sidekiq, karafka, and other async stuff with Gusto (see what I did there?)
job-boards.greenhouse.io/gus…
Josh Nichols retweeted
Stumbled across notes from my first @RenderATL (always keep your notes!). From @kishau :
"Don't create glass ceilings for yourself by being an ineffective communicator and not being a good storyteller... If you make a technology choice, be able to talk through the alternatives you didn't pick and why"
still true and relevant for my current role I didn't even have then🙏🏾
Josh Nichols retweeted
"None of our top 50 endpoints by throughput can have more than 5% of responses be >500ms"
This is a good perf goal. It scopes to the most important parts of the app, is set based on expectations of human users (users can feel >500ms resp), and it's usually quite achievable.
Josh Nichols retweeted
I just released Zeitwerk 2.7.
Now, you can define namespaces with manual constant assignments too (think, Namespace = Struct.new).
To the best of my knowledge, this completes the most important goal of the gem: to fully match Ruby. 🎉
Let me elaborate a bit on why that was not supported before, and how we have been able to remove that long-standing limitation.
First, let's take a step back to understand a tricky aspect of namespaces. Consider this Hotel namespace (in Ruby, both classes and modules can be namespaces):
# hotel.rb
class Hotel
include Pricing
end
# hotel/pricing.rb
module Hotel::Pricing
# Pricing related logic extracted to a mixin.
end
You cannot load any of the two files in regular Ruby, right? To load hotel.rb you need Hotel::Pricing in place, but if you try to load hotel/pricing.rb, you need Hotel!
But that works if the project is managed by Zeitwerk, and it worked with the classic autoloader too.
To support that use case, the :class event of TracePoint was instrumental, and it was the only technique I could think of in 2018.
It worked this way: When TracePoint notifies that Hotel has been created, the loader scans its subdirectories and defines autoloads (issues Module#autoload calls) for child constants, in particular for Hotel::Pricing.
The key observation is that happens precisely before the include line is executed. When the interpreter hits that line, the autoload is in place and autoloading hotel/pricing.rb is possible (because Hotel exists!).
Problem was, the event was not triggered for manual constant assignments (think Hotel = Class.new). So, while that was a corner case, unfortunately we could not achieve full parity with this technique.
The most important and common way to the define namespaces, using the class/module keywords, worked. But argghhhh.
But the suffering is over my friends!
In part motivated by this problem, @_byroot implemented Module#const_added. That callback is invoked in both situations! 🤘
Thanks to Module#const_added, the limitation has been finally removed. Also, Zeitwerk no longer uses TracePoint internally (/cc @yukihiro_matz).
Zeitwerk 2.7 requires Ruby 3.2 or later, and upgrading should be seamless for existing projects.
Don't worry if your app uses an older Ruby. Bundler realizes 2.7's Ruby version constraint is not satisfied, and ignores it in that case. Gemfile.lock will always get a version compatible with your app.
Same for gems that need to support Ruby versions older than 3.2, a version constraint for zeitwerk like ~> 2.6 will do the job. Client code will automatically use a compatible one.
Mission complete! 🎉
Josh Nichols retweeted
We're hiring engineer #4 at @scholarlysoft on-site in Seattle.
If that's you, let's chat! DMs are open.
scholarly.breezy.hr/p/a8959b…
Very exited and proud to be sponsoring #railsconf this year. I’ll be at the open source hackday working on singed, a performance debugging tool!
medium.com/gusto-engineering…
Josh Nichols retweeted
I have a long list of ideas I want to work on but not enough time. I do have some money though. Is it time for Speedshop's first employee?
If you are:
1. A good writer
2. A solid Rubyist that can build a product from start to finish
... maybe it's you?
Josh Nichols retweeted
Our system tests were slow and we couldn't figure out why... turned out it was a 1-line fix!
I've written this post for @GustoHQ's engineering blog: engineering.gusto.com/our-ru…











