I built a portable serial terminal for ESP32/Arduino in Rust — because my board kept resetting
If you debug ESP32 boards over serial, you probably know this one: you flash the firmware, rush to open a serial monitor to catch the boot logs… and the act of opening the monitor resets the board. The DTR/RTS lines toggle on connect, the ESP32 auto-resets, and the first lines you actually wanted are gone.
I hit this enough times that I built my own terminal.
CNTerminal
Live + download: https://www.coding-now.com/en/cnterminal Source (MIT): https://github.com/cflab2017/tool_serial_terminal_Rust
It's a free, portable serial terminal for Windows — a single ~8 MB .exe. No installer, no runtime, no admin rights: drop it on a USB stick, run it on a locked-down lab PC, done.
What it does:
DTR / RTS control — the reason it exists. Keep DTR from toggling on connect and the ESP32 doesn't auto-reset, so you actually see the boot output.
HEX send & receive — for binary protocols, plus an ASCII ↔ HEX converter built in (no more alt-tabbing to a converter site mid-debug).
5,000-line auto-trim — long logging sessions don't slowly eat your RAM or lag the UI.
The usual: port/baud selection, timestamps, a dark amber CRT theme that's easy on the eyes during long sessions.
Why Rust + egui
I wanted a single file people could run anywhere, which ruled out anything with a runtime. Rust + egui turned out to be a great fit: the whole GUI app compiles to one small native binary, immediate-mode UI keeps the serial read loop and the rendering loop simple to reason about, and serialport-rs handles the device side.
The trickiest part wasn't the UI — it was making the serial reader thread, the auto-trimmed scrollback buffer, and the GUI repaint cooperate without dropping bytes at high baud rates. If you're curious, the source is MIT and pretty small.
Honest scope
Windows only for now (single-exe portability was the goal).
It's a terminal, not an IDE plugin — it does serial I/O well and nothing else.
If you live in a serial monitor — Arduino, ESP32, STM32, industrial gear over RS-232 — give it a try and tell me what's missing compared to your current setup. Feature requests and bug reports are very welcome, here or on GitHub.
via DEV Community: rust (author: 코딩나우(하늘아래))
If you debug ESP32 boards over serial, you probably know this one: you flash the firmware, rush to open a serial monitor to catch the boot logs… and the act of opening the monitor resets the board. The DTR/RTS lines toggle on connect, the ESP32 auto-resets, and the first lines you actually wanted are gone.
I hit this enough times that I built my own terminal.
CNTerminal
Live + download: https://www.coding-now.com/en/cnterminal Source (MIT): https://github.com/cflab2017/tool_serial_terminal_Rust
It's a free, portable serial terminal for Windows — a single ~8 MB .exe. No installer, no runtime, no admin rights: drop it on a USB stick, run it on a locked-down lab PC, done.
What it does:
DTR / RTS control — the reason it exists. Keep DTR from toggling on connect and the ESP32 doesn't auto-reset, so you actually see the boot output.
HEX send & receive — for binary protocols, plus an ASCII ↔ HEX converter built in (no more alt-tabbing to a converter site mid-debug).
5,000-line auto-trim — long logging sessions don't slowly eat your RAM or lag the UI.
The usual: port/baud selection, timestamps, a dark amber CRT theme that's easy on the eyes during long sessions.
Why Rust + egui
I wanted a single file people could run anywhere, which ruled out anything with a runtime. Rust + egui turned out to be a great fit: the whole GUI app compiles to one small native binary, immediate-mode UI keeps the serial read loop and the rendering loop simple to reason about, and serialport-rs handles the device side.
The trickiest part wasn't the UI — it was making the serial reader thread, the auto-trimmed scrollback buffer, and the GUI repaint cooperate without dropping bytes at high baud rates. If you're curious, the source is MIT and pretty small.
Honest scope
Windows only for now (single-exe portability was the goal).
It's a terminal, not an IDE plugin — it does serial I/O well and nothing else.
If you live in a serial monitor — Arduino, ESP32, STM32, industrial gear over RS-232 — give it a try and tell me what's missing compared to your current setup. Feature requests and bug reports are very welcome, here or on GitHub.
via DEV Community: rust (author: 코딩나우(하늘아래))
Why I still teach Singleton even though modules make it redundant
Ask any developer what design pattern they know best and Singleton comes up first. Ask the same group if they use it in production and half will say no. The module system already does the job.
They're right. But I still include it in my patterns reference, and here's why.
What Singleton actually solves
The pattern ensures a class has only one instance and provides a global access point to it. Shared configuration loaded from env vars, a logger that buffers output, a connection pool you don't want duplicated. That kind of thing.
The classical JavaScript version looks like this:
Boilerplate. Every time.
The module alternative
In JavaScript and Python, a module is already a singleton. The runtime caches it after the first import. So the same Config above becomes:
Same object, every import, no class, no getInstance(). Python works identically.
So the pattern is "redundant" in the sense that you'd rarely write the classical version in a modern JS or Python codebase. You'd just export a module-level object.
Why I still teach it
Three reasons.
You will read it in older code. Codebases written before ES modules were standard, Python 2-era code, Java and PHP services. If you've never seen the pattern explained properly, you'll waste time figuring out what the class is doing.
It makes the intent explicit. A plain exported object and a Singleton both enforce one instance, but the Singleton makes that constraint visible and enforced at the type level. In teams, explicit beats implicit.
Rust does not have the shortcut. OnceLock (stable since 1.70) or LazyLock (stable since 1.80) is the idiomatic way to get a static singleton in Rust. There is no module-level trick.
That is the pattern, just dressed in modern syntax.
The broader point
This is what I tried to do with the whole reference. For each of the 23 GoF patterns, show the classical approach and then show what modern language features replaced or simplified it. Some patterns are still essential. Some you'd only write in a language without the shortcut. Some you'll only encounter as a reader, not a writer.
The reference covers all 23 patterns plus the 5 SOLID principles, with examples in JavaScript, Python, and Rust. MIT licensed.
https://github.com/dsheiko/design-patterns-for-web-developer
via DEV Community: rust (author: Dmitry Sheiko)
Ask any developer what design pattern they know best and Singleton comes up first. Ask the same group if they use it in production and half will say no. The module system already does the job.
They're right. But I still include it in my patterns reference, and here's why.
What Singleton actually solves
The pattern ensures a class has only one instance and provides a global access point to it. Shared configuration loaded from env vars, a logger that buffers output, a connection pool you don't want duplicated. That kind of thing.
The classical JavaScript version looks like this:
class Config {
static #instance = null;
#data;
constructor() {
this.#data = {
apiUrl: process.env.API_URL ?? "http://localhost:3000",
timeout: Number(process.env.TIMEOUT ?? 5000),
};
}
static getInstance() {
if (!Config.#instance) {
Config.#instance = new Config();
}
return Config.#instance;
}
get(key) {
return this.#data[key];
}
}
Boilerplate. Every time.
The module alternative
In JavaScript and Python, a module is already a singleton. The runtime caches it after the first import. So the same Config above becomes:
// config.js
export const config = {
apiUrl: process.env.API_URL ?? "http://localhost:3000",
timeout: Number(process.env.TIMEOUT ?? 5000),
};
// anywhere else
import { config } from "./config.js";
Same object, every import, no class, no getInstance(). Python works identically.
So the pattern is "redundant" in the sense that you'd rarely write the classical version in a modern JS or Python codebase. You'd just export a module-level object.
Why I still teach it
Three reasons.
You will read it in older code. Codebases written before ES modules were standard, Python 2-era code, Java and PHP services. If you've never seen the pattern explained properly, you'll waste time figuring out what the class is doing.
It makes the intent explicit. A plain exported object and a Singleton both enforce one instance, but the Singleton makes that constraint visible and enforced at the type level. In teams, explicit beats implicit.
Rust does not have the shortcut. OnceLock (stable since 1.70) or LazyLock (stable since 1.80) is the idiomatic way to get a static singleton in Rust. There is no module-level trick.
use std::sync::LazyLock;
static CONFIG: LazyLock<Config> = LazyLock::new(Config::from_env);
That is the pattern, just dressed in modern syntax.
The broader point
This is what I tried to do with the whole reference. For each of the 23 GoF patterns, show the classical approach and then show what modern language features replaced or simplified it. Some patterns are still essential. Some you'd only write in a language without the shortcut. Some you'll only encounter as a reader, not a writer.
The reference covers all 23 patterns plus the 5 SOLID principles, with examples in JavaScript, Python, and Rust. MIT licensed.
https://github.com/dsheiko/design-patterns-for-web-developer
via DEV Community: rust (author: Dmitry Sheiko)
Web Developer Travis McCracken on DevOps Tips from a Web Developer
via DEV Community: rust (author: Travis McCracken Web Developer)
via DEV Community: rust (author: Travis McCracken Web Developer)
Telegraph
Web Developer Travis McCracken on DevOps Tips from a Web Dev…
Exploring Backend Development with Rust and Go: Insights from Web Developer Travis McCracken As a passionate Web Developer specializing in backend systems, I’ve had the opportunity to work extensively with modern programming languages like Rust and Go. These…
5 Features That Make chematic Stand Out as a Pure-Rust Cheminformatics Library
via DEV Community: rust (author: kent-tokyo)
via DEV Community: rust (author: kent-tokyo)
Telegraph
5 Features That Make chematic Stand Out as a Pure-Rust Chemi…
I'm building chematic, a pure-Rust cheminformatics toolkit. Every library worth using—RDKit, OpenBabel, CDK—requires C/C++ at its core. chematic targets RDKit-level coverage with zero FFI: compiles to WASM, native binaries, and everything in between, without…
BoxAgnts Tool System (5) — WASM Tool Development: From Hello World to Production Deployment
via DEV Community: rust (author: Guyoung Studio)
via DEV Community: rust (author: Guyoung Studio)
Telegraph
BoxAgnts Tool System (5) — WASM Tool Development: From Hello…
WASM sandboxing provides BoxAgnts with instruction-level security isolation, while the tool registration chain enables zero-configuration auto-discovery. On top of these two foundations, developers only need to focus on one thing: writing programs that follow…
Tauri v2 Cheatsheet — The Commands I Use on Every Project
All tests run on an 8-year-old MacBook Air. All results from shipping 7 Mac apps as a solo developer. No sponsored opinion.
After 7 Tauri apps, I type the same commands constantly. Here's the reference I wish existed when I started.
Project setup
Development
Building
Plugins
This updates both
Permissions (tauri.conf.json)
Tauri v2 requires explicit permission declarations. If a command silently does nothing, check permissions first.
Common Rust patterns
Notarization (macOS)
Debugging
The most useful thing I learned
When a Tauri command silently fails: check the browser console first (
Silent failures in Tauri are almost always a permissions issue or a missing
TL;DR: Quick Tauri v2 command reference:
If this was useful, a ❤️ helps more than you'd think — thanks!
HiyokoAutoSync | X → @hiyoyok
via DEV Community: rust (author: hiyoyo)
All tests run on an 8-year-old MacBook Air. All results from shipping 7 Mac apps as a solo developer. No sponsored opinion.
After 7 Tauri apps, I type the same commands constantly. Here's the reference I wish existed when I started.
Project setup
# New project
npm create tauri-app@latest
# Add to existing project
npm install --save-dev @tauri-apps/cli
npx tauri init
Development
# Dev mode (hot reload)
npm run tauri dev
# Dev with specific log level
RUST_LOG=debug npm run tauri dev
# Dev with backend logs visible
npm run tauri dev 2>&1 | grep -v "^$"
Building
# Standard build
npm run tauri build
# Universal binary (Intel + Apple Silicon)
npm run tauri build -- --target universal-apple-darwin
# Debug build (faster, no optimization)
npm run tauri build -- --debug
Plugins
npm run tauri add global-shortcut
npm run tauri add fs
npm run tauri add shell
npm run tauri add notification
This updates both
Cargo.toml and the plugin registration. Faster than doing it manually.Permissions (tauri.conf.json)
{
"app": {
"security": {
"capabilities": [
{
"identifier": "main-capability",
"description": "Main window capabilities",
"windows": ["main"],
"permissions": [
"fs:read-all",
"fs:write-all",
"shell:execute",
"global-shortcut:allow-register"
]
}
]
}
}
}
Tauri v2 requires explicit permission declarations. If a command silently does nothing, check permissions first.
Common Rust patterns
// Get app data directory
let data_dir = app.path().app_data_dir().unwrap();
// Emit event to frontend
app_handle.emit("event-name", payload).ok();
// Get window
let window = app.get_webview_window("main").unwrap();
// App state
app.manage(MyState::new());
let state = app.state::<MyState>();
Notarization (macOS)
# Submit for notarization
xcrun notarytool submit app.dmg \
--apple-id YOUR_APPLE_ID \
--team-id YOUR_TEAM_ID \
--password YOUR_APP_PASSWORD \
--wait
# Staple after notarization
xcrun stapler staple app.dmg
Debugging
# Check what's in the bundle
unzip -l target/release/bundle/macos/App.app/Contents/MacOS/App
# Verify notarization
spctl -a -v App.app
# Check entitlements
codesign -d --entitlements - App.app
The most useful thing I learned
When a Tauri command silently fails: check the browser console first (
Cmd+Option+I in dev mode), then check RUST_LOG output, then check permissions.Silent failures in Tauri are almost always a permissions issue or a missing
#[tauri::command] registration.TL;DR: Quick Tauri v2 command reference:
npm run tauri dev / tauri build -- --target universal-apple-darwin / tauri add <plugin>. Silent command failures? Check DevTools console → RUST_LOG → permissions in that order. Almost always a missing permission or unregistered command.If this was useful, a ❤️ helps more than you'd think — thanks!
HiyokoAutoSync | X → @hiyoyok
via DEV Community: rust (author: hiyoyo)
BoxAgnts Tool System (6) — Multi-Provider Adaptation and the Agent Query Loop
via DEV Community: rust (author: Guyoung Studio)
via DEV Community: rust (author: Guyoung Studio)
Telegraph
BoxAgnts Tool System (6) — Multi-Provider Adaptation and the…
BoxAgnts' tool system, from the bottom-level WASM sandbox to the top-level Tool trait, has solved "how tools run safely." But tools ultimately need to be called by AI models — which introduces two engineering problems: the complete incompatibility of API…
Bun rewrote itself from Zig to Rust in 9 days with an LLM. That's terrifying.
via DEV Community: rust (author: Aditya Agarwal)
via DEV Community: rust (author: Aditya Agarwal)
Telegraph
Bun rewrote itself from Zig to Rust in 9 days with an LLM. T…
It took nine days to rewrite all of Bun from Zig to Rust using an LLM and get the new code to the point where I could merge it. I saw that and my reaction was not “cool.” It was “oh no.” Let me be clear. The speed isn't what scares me. The confidence is.…
RustRover Autocomplete Issues with Macro-Heavy Assertion Libraries Solved by RXpect's Trait-Based Approach
via DEV Community: rust (author: Sergey Boyarchuk)
via DEV Community: rust (author: Sergey Boyarchuk)
Telegraph
RustRover Autocomplete Issues with Macro-Heavy Assertion Lib…
Introduction Rust's assertion libraries have long been a double-edged sword for developers. While macros provide syntactic sugar and concise syntax, their heavy use introduces a critical friction point: they disrupt RustRover's autocomplete functionality.…
Web Developer Travis McCracken on The Case Against Too Many Microservices
via DEV Community: rust (author: Travis McCracken Web Developer)
via DEV Community: rust (author: Travis McCracken Web Developer)
Telegraph
Web Developer Travis McCracken on The Case Against Too Many …
Harnessing the Power of Rust and Go for Backend Development: Insights from Web Developer Travis McCracken As a passionate Web Developer, I’ve always believed that choosing the right tools for backend development can make or break the scalability, performance…
Building a self-hosted, AI-native workflow engine in Rust (180 node types, no SDK bloat)
via DEV Community: rust (author: 元方)
via DEV Community: rust (author: 元方)
Telegraph
Building a self-hosted, AI-native workflow engine in Rust (1…
I've spent the last while building Trigix — an open-source (MIT), self-hostable workflow automation platform. Think n8n, but the execution engine is in Rust and the AI nodes can run entirely against local models. This post is about a few engineering decisions…
I built a safety-first AI IDE in Tauri/Rust – local, free, $0 (7 months solo)
via DEV Community: rust (author: Amritanshu Amar)
via DEV Community: rust (author: Amritanshu Amar)
Telegraph
I built a safety-first AI IDE in Tauri/Rust – local, free, $…
Yesterday, Cursor was acquired by SpaceX for $60 billion. I've been building a free, local-first AI IDE for 7 months. Alone.No VC money. No team. No MIT network. This is that story. Why I built PunamIDE Most AI IDEs are built around one idea: code faster.…