Job postings that came across my desk, slack, email, discord, etc this week.
The post Job listings for week ending 8/14 appeared first on Leon Adato.
Job postings that came across my desk, slack, email, discord, etc this week.
The post Job listings for week ending 8/14 appeared first on Leon Adato.
When the pandemic hit and nobody could travel anywhere, I, like many others, found connections through Zoom calls. I had multiple scheduled calls with different groups: a daily morning call, a weekly Friday call, two board game groups, a poker night, a Call of Duty group, and semi-regular calls with various individuals. I am very grateful for the friends I have made over the years and that those friends were there over the pandemic helped avoid the feelings of isolation many felt.
Now that most people have moved back into old (or perhaps new) routines, the regular online calls have mostly disappeared. And with a decision pre-pandemic to stop speaking at conferences, I haven’t been attending them, either. That has meant missing out on one particular avenue of connecting and reconnecting with people that I’ve known for years. Long gone are the days of hanging out in the hallway of a conference chatting with people in between sessions. Or going out for tacos and then to a bar where somebody is dancing in a head-to-toe Batman costume. (Yes, that happened.) But even conference attendance is down.
Pre-pandemic, I’d often be visiting a city and then put out word on Twitter that I’d enjoy meeting up with people and sure enough, I’d be at a table with a handful of fellow web practitioners enjoying a lovely meal and listening to their experiences and sharing mine.
The scattering to the smattering of social networks along with those abandoning social media altogether has made this, too, more difficult. It takes a more concerted effort to stay connected. Yet Another Social Media Site doesn’t seem like it’s going to solve this.
This was one of the factors that spurred me to re-engage with social media: to maintain these loose connections, albeit perhaps more loosely than before.
I have close friends with whom I speak to near daily. I have friends with whom I speak with every few months. And then I have those people with whom I’d normally have connected with in person and those in-person connections are happening less and less.
I don’t know that I have any answers. I don’t know whether this is even a “problem” that needs to be fixed. Perhaps it’s just a fact of life that some connections are lost to time.

The Angular ecosystem continues to push forward with deeper AI integration and modern form architectures. This week, our community experts track the complete evolution of form handling, explore client-side AI, and dive into tactical discussions about developer craftsmanship in the age of AI.
Catch up with these incredible community features!
Sonu Kapoor @SonuKapoor1978 charts the complete architectural evolution of forms in Angular. Learn how form state management has progressed from legacy template-driven approaches all the way to the highly reactive, modern Signal Forms API.
Christian Liebel (@christianliebel) l breaks down the latest advancements in client-side machine learning. Discover how you can leverage powerful Web AI capabilities directly inside browser environments to build faster, privacy-focused web experiences.
Brooke Avery @JediBravery and Matthew Christiansen drop three stellar episodes of The Dev Life podcast covering architecture and real-world AI strategies. First, they host Sander Elias to map out v21 vision and velocity from an architect’s lens. Next, legendary author Dave Thomas drops by to discuss developer craftsmanship as a “Pragmatic Agent.” Finally, Alfredo Perez joins the show to share favorite tools and hard-earned lessons from using AI in the trenches.
Ready to practice? Thomas Laforge (@laforge_toma) provides an excellent set of coding challenges to sharpen your skills. Move from building a simple signal form to handling complex nested configurations and dynamic form arrays. Take the challenges: https://angular-challenges.vercel.app/challenges/forms/64-form-array/
How are you adapting your development craftsmanship to include AI tooling this year? Have you solved a complex form array problem using the new primitives?
Keep the momentum going! Use #AngularSparkles to amplify these expert tutorials and keep the community learning 👇
The Forms Evolution, Web AI, and Architectural Vision! 🧬🤖 was originally published in Angular Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.
Firmware development has always been challenging: mismatched toolchains, “it works on my machine” builds, and the tension between maintaining legacy products and shipping new features. In this article we explore how you can use Docker and Docker sandboxes to ease firmware development, especially for ESP32 projects. Nowadays, teams end up supporting multiple hardware revisions, several ESP-IDF releases, and long-term customer deployments, all while iterating on new capabilities like Wi-Fi 6, Matter, or power optimizations.
The official espressif/idf Docker image solves the reproducibility problem. Docker Sandboxes (the sbx CLI) solve a newer one: letting AI coding agents work on your firmware at full speed without giving them the keys to your laptop. This article walks through a practical workflow that combines both: clean builds, parallel environments for new and legacy firmware, and safe unsupervised AI sessions.
The espressif/idf image ships a complete, pinned ESP-IDF installation: the framework itself, the Xtensa/RISC-V toolchains, Python environment, CMake, ninja, everything. A build needs one command:
docker run --rm -v $PWD:/project -w /project \
-u $UID -e HOME=/tmp \
espressif/idf:release-v5.4 idf.py build
A few details worth understanding rather than cargo-culting:
-u $UID -e HOME=/tmp makes the container run as your user, so build artifacts in build/ aren’t owned by root. HOME=/tmp gives the IDF tools a writable home for their caches.latest tracks the master branch and will break you eventually. vX.Y tags are fixed releases; release-vX.Y tags track the release branch and receive bugfixes. For products in maintenance, exact vX.Y.Z tags are the safest; for active development, release-vX.Y is a good balance.-e IDF_GIT_SAFE_DIR='/project' to whitelist the path (use : to separate multiple paths).-e IDF_CCACHE_ENABLE=1 and persist it across runs by mounting a volume for it. Full rebuilds of a mid-size project drop from minutes to seconds.On Linux, pass the serial device through:
docker run --rm -it \
--device=/dev/ttyUSB0 \
--group-add $(getent group dialout | cut -d: -f3) \
-v $PWD:/project -w /project \
-u $UID -e HOME=/tmp \
espressif/idf:release-v5.4 idf.py flash monitor
The --group-add is needed because you’re running as $UID, not root, and the device node belongs to dialout.
On macOS and Windows, Docker Desktop cannot pass USB devices into containers. The clean workaround is a network serial bridge using RFC2217, which esptool supports natively. On the host:
pip install esptool
esp_rfc2217_server -p 4000 /dev/cu.usbserial-1420
Inside the container, point idf.py at the network port:
idf.py --port 'rfc2217://host.docker.internal:4000?ign_set_control' flash monitor
This looks like a hack but it’s actually a feature: once the serial port is a network endpoint, anything can reach it. Containers, CI runners, and (as we’ll see) sandboxed AI agents. Keep this trick in mind; it’s the linchpin of Part 3.
Nobody should type these commands twice. A small Makefile keeps the interface stable even if the plumbing changes:
IDF_IMAGE ?= espressif/idf:release-v5.4
PORT ?= /dev/ttyUSB0
DOCKER_RUN = docker run --rm -it \
--device=$(PORT) \
--group-add $(shell getent group dialout | cut -d: -f3) \
-v $(PWD):/project -w /project \
-v idf-ccache:/ccache -e CCACHE_DIR=/ccache -e IDF_CCACHE_ENABLE=1 \
-u $(shell id -u) -e HOME=/tmp -e IDF_GIT_SAFE_DIR=/project \
$(IDF_IMAGE)
build:
$(DOCKER_RUN) idf.py build
flash:
$(DOCKER_RUN) idf.py flash
monitor:
$(DOCKER_RUN) idf.py monitor
menuconfig:
$(DOCKER_RUN) idf.py menuconfig
shell:
$(DOCKER_RUN) bash
Now make build works identically for every developer and in CI, and switching IDF versions is make build IDF_IMAGE=espressif/idf:release-v5.3.
This is where the container approach stops being merely convenient and starts changing how you work. Because each container is fully isolated, you can run two different IDF versions against two different boards at the same time, on the same machine.
# Terminal 1 - new feature branch, IDF 5.4, experimental board
docker run --rm -it --device=/dev/esp32-experimental \
-v $PWD/new-feature:/project -w /project \
-u $UID -e HOME=/tmp \
espressif/idf:release-v5.4
# Terminal 2 - legacy firmware, IDF 5.3, production board
docker run --rm -it --device=/dev/esp32-production \
-v $PWD/legacy:/project -w /project \
-u $UID -e HOME=/tmp \
espressif/idf:release-v5.3
Typical uses: flashing experimental code on one board while a long-running soak test or customer demo stays untouched on the other; A/B-comparing power consumption between firmware versions; reproducing a field bug on the exact legacy toolchain while the fix is developed on the current one.
/dev/ttyUSB0 and /dev/ttyUSB1 swap depending on plug order, which will eventually make you flash the wrong board. On Linux, pin them with udev rules keyed on the adapter’s serial number:
# find the serial numbers
udevadm info -a /dev/ttyUSB0 | grep '{serial}'
# /etc/udev/rules.d/99-esp32.rules
SUBSYSTEM=="tty", ATTRS{serial}=="A50285BI", SYMLINK+="esp32-experimental"
SUBSYSTEM=="tty", ATTRS{serial}=="B7743NM0", SYMLINK+="esp32-production"
After udevadm control --reload, the symlinks survive reboots and re-plugs, and your Makefile targets can reference boards by role instead of by enumeration accident.
If the two-environment setup is permanent, a compose.yaml documents it better than shell history:
services:
new-feature:
image: espressif/idf:release-v5.4
volumes: ["./new-feature:/project"]
working_dir: /project
devices: ["/dev/esp32-experimental:/dev/ttyUSB0"]
stdin_open: true
tty: true
legacy:
image: espressif/idf:release-v5.3
volumes: ["./legacy:/project"]
working_dir: /project
devices: ["/dev/esp32-production:/dev/ttyUSB0"]
stdin_open: true
tty: true
docker compose run new-feature idf.py flash monitor and the mapping from role to physical board is version-controlled.
Coding agents like Claude Code are genuinely useful for firmware work: porting components between IDF versions, writing unit tests, chasing config drift in sdkconfig. But to be useful they need to run things: builds, flashes, pip install, sometimes Docker itself. Giving an agent that freedom directly on your host, in bypass-permissions mode, is uncomfortable for good reasons.
Docker Sandboxes solve this with a stronger primitive than a container: each sandbox is a microVM with its own kernel, filesystem, network stack, and its own private Docker daemon. The agent can install packages, modify system config, build and run containers, and none of it touches your host. Your workspace directory syncs into the sandbox at the same path, so file paths in error messages match between the two worlds.
The CLI is small and clear:
# start Claude Code in a sandbox for the current project
sbx run claude
# work on a specific directory
sbx run claude ~/firmware/new-feature
# see what's running, resource usage, network requests
sbx
# list and clean up
sbx ls
sbx rm new-feature
Three properties matter for firmware work in particular:
sbx rm and it never happened. Your host IDF setup, if you even have one, is untouched.curl your firmware to somewhere unexpected simply can’t.Here’s where the RFC2217 trick from Part 1 pays off. The sandbox is a VM; there is no USB passthrough. But there is a network path to the host. So expose the serial port as a network service on the host:
esp_rfc2217_server -p 4000 /dev/esp32-experimental
and tell the agent (in your project’s CLAUDE.md or equivalent) to flash with:
idf.py --port 'rfc2217://host.docker.internal:4000?ign_set_control' flash monitor
Now the agent’s whole loop runs end-to-end inside the sandbox: edit, build in a container it spawned itself, flash real hardware, read the monitor output, fix the bug. The only thing it can reach on your machine is one serial port you explicitly published. That’s a remarkably good trade: full hardware-in-the-loop autonomy, minimal blast radius.
Run one sandbox per board and you get the parallel-environment pattern from Part 2, agent edition: an agent iterating on the experimental board via port 4000 while you, or a second locked-down agent, watch the production board via port 4001.
Sandboxes are newer technology than containers, and it shows in places. MicroVM isolation is available on macOS (Apple Silicon), Windows 11, and Linux with KVM. Build performance inside the microVM is noticeably slower than native containers: fine for agent sessions, annoying for your own tight inner loop. And the agent runs in bypass-permissions mode by design; the isolation is the permission system, so review the diff before merging, same as you would for any contributor.
espressif/idf image (plus the Espressif IDF extension inside the container). Same image as CI, full IntelliSense, native-container speed.sbx run claude --branch <feature>. The branch flag keeps the agent’s commits on a worktree, so your checkout stays clean; review and merge when it’s done.esp_rfc2217_server per board.espressif/esp-idf-ci-action, pinned to the same IDF version as your dev image. If a build passes locally, it passes in CI. It’s the same bits.
# .github/workflows/build.yml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { submodules: recursive }
- uses: espressif/esp-idf-ci-action@v1
with:
esp_idf_version: v5.4
target: esp32s3
release-v5.4, not latest), and record the tag in the repo (Makefile or compose file) so the toolchain version is part of the code review.new-feature/, legacy/) with its own pinned image. Never share a build/ directory between IDF versions.IDF_GIT_SAFE_DIR=/project kills the Git ownership warnings; IDF_CCACHE_ENABLE=1 plus a ccache volume kills the rebuild times.--group-add for the dialout GID when combining --device with -u $UID.CLAUDE.md so agents discover the hardware setup without being told each session.FROM espressif/idf:release-v5.4 rather than installing them in every session.Docker turned ESP32 builds from a fragile, machine-specific ritual into something reproducible enough to trust. Parallel containers turn one desk into a small hardware lab, with legacy and next-gen firmware coexisting without friction. And Docker Sandboxes close the last gap: they make it reasonable, not reckless, to hand an AI agent a real board and let it work.
If you’re still installing ESP-IDF directly on your host machine in 2026, you’re working harder than necessary. Try the two-board setup this week: new firmware iterating on one device, stable firmware soaking on the other. Then hand one of them to an agent in a sandbox and see how far it gets.
Happy hacking!