Valentyn Kit

Valentyn Kit

@valentynkit.com

Systems engineer. ~6 yrs production backends; distributed systems in C & Rust. I build the smaller, wronger version of everything I depend on, to see how the real one works. Heading toward Solana protocol.

17 followers20 following55 posts

Spent the week reading readahead heuristics written before I started programming. They still guess right on hardware that did not exist when they were written. There is a kind of care in old kernel code that I do not often find in what I have shipped for money.

A Rust one. You lock a mutex but bind the guard to _, then lock the same mutex again in the same scope. Does it deadlock? Worth checking whether it gets that far.

Measured this morning: one 4 KB read at page 0 of a cold file leaves 4 pages resident. The same read at page 1 leaves 1. No read at all leaves 0. I went hunting for a bug in my own mincore tool and found a kernel heuristic instead.

The offset-0 special case lives in mm/readahead.c. A read starting at byte zero is taken as "they are about to stream this", so the window gets seeded straight away. Any other offset is random access until a pattern proves otherwise.

Every feature a language adds is one more thing every future reader of your code has to know, and that cost never shows up in the benchmark. Simple and easy aren't the same word: easy is familiar, simple is one idea not tangled with three others.

The honest measure of a language is how much of it you can hold in your head. I hold most of Go, not all of Rust, and nobody holds all of C++. The problem is never complexity you chose on purpose. It's the complexity you absorbed by default that buys nothing.

The compiler is the opposite of an LLM, and I think people are starting to use it like one. An LLM guesses: same question, two answers, most confident when it's wrong. A compiler proves: same input, same answer, and it points at the exact line.

Mash build, skim the error, tweak, rerun until green, and you're brute-forcing an oracle that already handed you the proof. As the rest of the stack goes probabilistic, the one tool that can't bluff you gets more valuable, not less.

A Rust one for you: iterate a HashMap with a for loop, and inside the loop remove the exact key the loop is currently on. Does it compile? If you think it does, what happens when you run it?

You don't understand a system until you've rebuilt a worse version of it. Using it teaches you the interface. Rebuilding it teaches you the constraints, and the constraints are where understanding lives.

Pick the tool you use most and build a toy of it. A database, a shell, an allocator. It doesn't have to be good. It has to be yours. The bug I hit at 1am in my toy was the design decision the real authors had agonized over.

Rust has no null. Tony Hoare called null his billion-dollar mistake, and Rust just left it out. A value that might be missing gets a different type: Option, not String, and the compiler won't let you use one as the other.

let name: String = find_user(id) doesn't compile: expected String, found Option. Every language with null lets you skip that and meet the None in production. Rust moves it to compile time.

Same program, two outputs: 255u8 + 1 panics under cargo build, prints 0 under --release. Defined wrap, not UB. overflow-checks = true if you want release to panic too. wrapping_add if the wrap is the point.

Rebuilding my Redis clone's AOF in Rust. BufWriter erases the buffering discipline C forced on me. But flush() only reaches the kernel, sync_all() is what reaches the disk, and Drop's automatic flush ignores its error. The mechanics compile away; the durability policy doesn't.

Vec::new() never touches the heap. Capacity stays 0 until the first push, then 4 for small types, doubling after. Free to create, pay on first write. I'd used it for years without checking where the first allocation actually lands.

I built a small Redis clone in C: RESP parser, command table, append-only file, the works. Now I'm rebuilding the same thing in Rust. Redoing a finished project has been the best language learning I've done.

When the design is already solved, the language is the only variable left. I'm not relearning how a key-value store works. I got that in C. So every difference I hit now is signal: where ownership reshapes my design, where std hands me what I hand-rolled by pointer in C.

Concrete one: in C the AOF is a raw fd, a manual buffer, and fsync error checks I have to remember. In Rust it's a BufWriter, the ? operator on every write, and Drop closing the file when it leaves scope. The failure I had to discipline myself into, the compiler now just enforces.

I built a small Redis clone in C: RESP parser, command table, append-only file, the works. Now I'm rebuilding the same thing in Rust. Redoing a finished project has been the best language learning I've done.

When the design is already solved, the language is the only variable left. I'm not relearning how a key-value store works. I got that in C. So every difference I hit now is signal: where ownership reshapes my design, where std hands me what I hand-rolled by pointer in C.

Concrete one: in C the AOF is a raw fd, a manual buffer, and fsync error checks I have to remember. In Rust it's a BufWriter, the ? operator on every write, and Drop closing the file when it leaves scope. The failure I had to discipline myself into, the compiler now just enforces.

I'm learning Rust by writing C. Backwards, I know. But every Rust feature answers a specific C pain, and you don't feel the answer until you've felt the pain. Write the manual error check 50 times in C, and ? stops being syntax and starts being relief.

I spent a week fighting "expected String, found &str" before it clicked. String owns its bytes on the heap and can grow. &str borrows a view into bytes someone else owns. The rule that ended it: take &str in args, return String when you built it.

A week of C reminded me what one character buys in Rust: the ? Every C syscall ends the same. Returned -1? Read errno, decide fatal or retry, handle it. Copy-pasted around every read and write. Rust folds that whole ritual into ?, a check you can't forget.

I froze my own TCP server for 1.51 seconds with one idle connection. No load. One client just holding the line. A second connected and waited 1.5s for a reply. That's when I actually understood why event loops exist. Not from a blog, from breaking it myself.

"Rust is hard" is mostly the first time a language makes you be honest about who owns your data. C didn't make ownership simpler. It just let you ship the bug and find out in production.

"How do you serve 10,000 clients with one program?" A 25-year-old question, the C10K problem. The answer is a ladder of I/O models. Each rung fixes the wall the last one hit. I rebuilt all seven in C this week, blocking up to epoll, to feel every wall myself.

"Rust is hard" is mostly the first time a language makes you be honest about who owns your data. C didn't make ownership simpler. It just let you ship the bug and find out in production. #rust

The Rust borrow checker clicked when I stopped reading it as a style rule and started reading it as a use-after-free detector at compile time. Borrow an element of a Vec, then push to it. The push can reallocate the buffer, so your reference would dangle. C compiles that. Rust won't.

The shift: a borrow error isn't the compiler being picky. It's the compiler catching a bug you can't see yet. Read each one as 'this would have dangled' and you stop fighting it. #rustlang

I've started contributing to rust-lang, deliberately on the low-level I/O side. Two PRs merged in the past couple of weeks. The latest: a new io::ErrorKind::TooManyOpenFiles. Before it, running out of file descriptors (EMFILE/ENFILE) decoded to a generic Uncategorized.

Now it's a first-class error kind, mapped across unix, wasi, and windows, so you can match err.kind() instead of parsing raw OS error numbers. The first PR documented the transient errors TcpListener::accept returns. Same corner of std. Making it a habit.

Valentyn Kit reposted

one odd thing about LLMs with programming is like... okay so obviously we all construct English and other human languages and sentences, and it learns from them when it talks but seeing code patterns that it learned that i specifically had a hand in creating is like, wild

You are up to date.