Returning to Rust: A Journey Through Tooling, Performance

When I started tackling the Maelstrom challenges, my initial thought was to use C++. It’s a language I know inside out, and its performance is hard to beat. However, as I contemplated setting up the project, I realized I couldn’t justify fighting with the C++ pipeline for free. Crafting a proper CMake or Bazel configuration might be worthwhile for large-scale projects or when compensated, but for personal experiments? It’s an unnecessary headache.

Why Go is My Default Choice

For most non-performance critical scenarios, Go is my default, no-brainer choice. It has a clean build system, excellent tooling, and a developer experience that doesn’t make me dread the setup process. Go’s simplicity allows me (and any level team) to focus on solving the problem rather than wrestling with the environment. Yet, this time, I decided to take a different path.

Revisiting Rust

My relationship with Rust has been a journey of highs and lows. I was initially drawn to it during its early days, around Rust 0.1, when the language was brimming with potential. What captivated me back then was its promise to be a kind of “Erlang on steroids”—a language that combined Erlang’s lightweight concurrency model and fault-tolerant programming paradigms with the performance and control of a systems language.

Unfortunately, with the release of Rust 1.0, much of that original vision shifted. Green threads—a key feature that enabled lightweight concurrency and aligned closely with the “Erlang on steroids” dream—were abandoned. The decision to remove them in favor of direct integration with the OS threading model made sense for speeding up the 1.0 release. Still, it marked a significant departure from the language’s earlier aspirations.

Adding to the change was a complete overhaul of Rust’s memory model, which moved toward its now-iconic ownership system. While the new model introduced unparalleled safety and performance guarantees, it also made the language more complex and less approachable, particularly for those who were excited about its initial simplicity.

These shifts left me feeling that Rust had transformed into “just another overcomplicated C++-like language,” albeit with better safety features and tooling. Over time, I moved away from Rust, drawn instead to more straightforward and developer-friendly languages for most of my work.

But now, years later, I’m giving Rust another shot while having plenty of free time. Its strong presence in emerging fields like Web3 has made it impossible to ignore. The language has clearly matured, and while its evolution may not have aligned with my initial hopes, I’m curious to explore how its modern ecosystem can contribute to my work. Rust may no longer be the “Erlang on steroids” I once envisioned, but it’s carving out a unique space for itself, and I’m eager to see how it fits into my technical journey moving forward.

Comparing Tooling and Developer Experience

My return to Rust brought some pleasant surprises—and a few frustrations. Here’s a breakdown of my observations:

Cargo vs. Build Systems

  • Cargo, Rust’s build system and package manager, is undeniably better than CMake—but then again, that’s not a high bar. Let’s be honest: CMake is an exercise of frustration for most developers.
  • Comparing Cargo to Bazel: Cargo feels more straightforward and approachable, but it requires more power and flexibility than Bazel. In that sense, Bazel is still the gold standard for me in large, complex projects.
  • Go’s build system, however, remains my favorite. Its simplicity and convention-over-configuration philosophy are unmatched.

IDEs and Editor Support

Tools like RustRover have evolved to deliver an exceptional development experience, far surpassing the likes of CLion or most other C++ IDEs. The level of integration with Cargo, code analysis, and developer assistance is simply stellar.

Compiler Diagnostics

One of Rust’s standout features is its compiler diagnostics. The clarity and detail in Rust’s error messages are leagues ahead of what you get with Clang or GCC. It’s an area where Rust truly shines, making debugging and iteration smoother. However, it’s not all perfect.

When it comes to borrowing-related errors, the story changes. These errors often feel nonintuitive, even for developers familiar with Rust’s ownership model. Despite the compiler’s best efforts to provide explanations, deciphering and resolving these errors frequently requires external help. You’ll find yourself Googling extensively or turning to tools like ChatGPT (model 4.0 preferred) to figure out what’s going wrong and how to fix it. This aspect of the Rust learning curve remains a significant hurdle for newcomers and a source of frustration even for experienced developers.

While the error diagnostics are still better than those offered by C++, the borrowing system’s complexity can sometimes undermine the excellent developer experience.

Reflections and Future Plans

Sometimes, stepping back into an ecosystem with a fresh perspective can reveal how much has changed—for better or worse.
Rust is no longer just the “overcomplicated” C++ sibling I left behind. It’s still a language with comparable complexity to C++ (which means it’s overcomplicated) but with a critical difference: compile-time sanitizers you need in C++ baked into its Rust core. This feature transforms those complexities into tools that actively guide you toward safer, more robust code.

Last but not least, as I’d mentioned above, I do not want to setup a proper C++ pipeline either

Minimal CI for Go library with GitHub actions

Continuous Integration (CI) has become an essential part of modern software development, and for good reason. It ensures code quality, speeds up development, and catches potential issues early. However, you can get started without an elaborate CI setup. Even a minimal CI setup can significantly improve your workflow. Here’s why every project should have at least minimal CI and how to implement it effectively using GitHub Actions.

What Constitutes Minimal CI?

For a project to benefit from CI without excessive complexity, it should include the following essential components:

1. Project Compilation: Verify that the codebase is always in a buildable state.
2. Unit Test Execution: Ensure the core functionality works as expected.
3. Static Code Analysis: Catch bugs and enforce coding standards before they become an issue.

Continue reading

GFSM: A Simple and Fast Finite State Machine for Go

Design patterns are not widely used in Go, as they can often lead to unnecessary complexity in the codebase. However, the Finite State Machine (FSM) is an exception that proves to be incredibly useful. When I set out to design GFSM, I aimed to create a fast and straightforward FSM implementation for Go.

I initially sought a quick and straightforward FSM solution for Go, but I couldn’t find anything that met my needs. Drawing inspiration from the speed-focused and minimalistic principles of C++ while remaining true to Go’s idioms, I developed GFSM to fill this gap. The outcome is GFSM—a library that distinguishes itself from alternatives like looplab/fsm by prioritizing speed and simplicity.

Whether orchestrating microservices, handling distributed systems, or designing embedded systems, GFSM brings the reliability and efficiency needed to keep things running smoothly.

Real-World Use Case: Two-Phase Commit Protocol

One of the best showcases for FSMs is the Two-Phase Commit protocol (TPC), widely used in distributed systems to ensure atomic transactions. Let’s explore how GFSM can model this protocol step by step.

Continue reading

Reflections on Reading “Introduction to Reliable and Secure Distributed Programming”

I’ve had Introduction to Reliable and Secure Distributed Programming sitting on my bookshelf for years, silently whispering, “Read me when you have time.” Of course, “later” always seemed like the right time. As software developers, we often prioritize practical, hands-on books that help solve immediate problems—topics like Kubernetes, Kafka or mastering another layer of C++ intricacies. But in hindsight, neglecting foundational theory is a mistake.

I began my journey with distributed systems over a decade ago. Back then, I wish someone had handed me this book and insisted I dive in immediately. It’s the kind of resource that can help set the foundation for anyone venturing into the complexities of distributed computing. Instead, I learned through trial, error, and practical exposure, which, while valuable, left gaps that only became apparent when I finally picked up this book. Continue reading

Sabbatical and Distributed Systems

Working at a unicorn company for four years was an exhilarating experience, though incredibly exhausting. Tackling significant projects that few have ever attempted means Google can’t offer much help. Surrounded by highly talented colleagues, many of whom you may never encounter again in one place, all while facing a relentless pace and high expectations from leadership, it eventually takes a toll. After a while, you just need to pause, rest, and reflect. That’s why I left Motional—to take several months to recharge, spend time with family, and reconnect with my hobbies.

View from the rooftop of the apartment, where we settled down. I love running up on the left side of the river—quite a nice trail run.

The irony is, after more than 20 years in IT, I’ve become somewhat of a workaholic, so proper rest eludes me. Just last weekend, I found myself speaking at the DevFest ’24 conference, sharing insights on best practices in project development with the local IT community. The event exceeded all my expectations. It’s been incredibly gratifying to see how much Kyrgyzstan’s IT sector has grown since I left in 2003. I sincerely hope this growth continues and that, eventually, talented engineers will want to stay here or return to the country instead of trying themselves outside, as I did. However, I’m also acutely aware that I’m now overqualified for most roles in the Kyrgyz job market, which is a bit bittersweet. So, my journey will likely inevitably take me elsewhere after my sabbatical. Continue reading

TIL: how to debug randomly hanging Python applications

Usually, if a Python-based application hangs, you either read logs or grab one of the PBD-based solutions, attaching to the application, and uses the Python console for investigation. The approach is straightforward; for example, you installed pdb-attach, and add a few lines to your application:

import pdb_attach
pdb_attach.listen(50000)

and expect that “magic” will just works:

> python -m pdb_attach <PID> 50000
(Pdb) YOU HAVE PDB SESSION HERE

But sometimes, magic is broken, and my theory is (I didn’t search for proof) that this is due to GIL. So, sometimes, no PDB prompt after you have attached to the application with PDB. In my case, the application hang in the multiprocessing.Process call where I used a gRPC server. The gRPC server didn’t react to the termination request, the process cannot stop, and like aggravating circumstances, all these are a part of PyTest that hang 1 of 20 executions.

This is a general PDB-based debuggers issue, which means all other tools like pyrasite-shell and PyTest PBD integration also don’t work. The only option here is GDB for Python, which is surprisingly amazing! First of all, you need to install Python extension for GDB.

sudo apt-get install python3.9-dbg

Then you can connect to your Python application which is a regular Python process with GDB, and explore the call-stack!

> gdb

(GDB) attach <PID>
(GDB) py-bt

If you use not APT-based Linux, search for proper instruction here.

Чего ожидать от работы в Self Driving?

Когда я только шел в AV два года назад, я думал, “а как оно там?”. Было ощущение, что AV это почти полностью про компьютерное зрение и прочее AI, т.е. ну совсем не моё и что лично мне там делать был большой вопрос. Но на деле оказалось, что это сильно не так, около 90% работы в данном домене вообще никак не связанны с AI задачами. На сегодня есть ощущение что у меня сложилась относительно полная картина о том, что же может ждать разработчика в автономных каретах. Тут я не планирую вдаваться ни в какие технические решения, а просто приведу общий обзор потенциальных направлений работы для IT-специалистов в данном домене.

Направления работ

Основных направлений работа несколько: автономный стек, инфраструктура, железо, безопасность, облака и аналитика. Само собой есть еще множество более мелких направлений, я выдели самые крупные. Наверное самыми неожиданными в списке будут два последних пункта, но без них реально никуда.

Автономный стек

В автономный стек как раз и входит всё то, что приходит на ум когда ты слышишь “работа в AV”. В то же время для меня это наименее знакомая и интересная часть. Извините
Continue reading

Возвращение к C++

Последние лет 6 я всё меньше и меньше занимался разработкой на C++, количество использования фактически шло по убывающей до нулевого значения год назад. Чего только не было в качестве основного языка, и Go, и Elixir и даже небольшие отрезки времени чистый Python. Но так случилось, что с переходом на новое место работы C++ вновь стал моим основным рабочим инструментом. Появилась насущная необходимость шустро освежить знания в голове и, в идеале, совместить это с чем-то полезные. В итоге я решил решил порешать задачки на тему криптографии от cryptopals дабы как-то скрасить ожидание открытия заинтересовавшего меня курса по криптографии на Коурсере.  Ну а так как теперь мне действительно есть с чем сравнивать C++, ощущения от языка получаются более цельные, как мне кажется. Continue reading

Elixir, backend и стартапы

Несмотря на то, что компания TSC просто замечательная, задержаться в ней надолго по ряду причин не вышло. Это был один из тех редчайших случаев когда действительно вся команда высоко квалифицирована, не менее высоко мотивирована и ответственна. За прошедшие 7 месяцев мне удалось посмотреть и потрогать руками практически новый для меня мир экзотических языков в prod-е, немного иначе посмотреть на то, какой бывает разработка backend-ов и попробовать на практике что такое стартап.

Elixir

Начнем с самого главного, а именно с Elixir, который до моего прихода был фактически единственным языком используемым в компании для разработки backend-а. На первый взгляд, это был разумный и продуманный выбор – простой и выразительный язык с кучей возможностей, дружелюбное сообщество и BEAM под капотом. Благодаря наличию Ecto и Phoenix разработка backend проходит относительно гладко и быстро, а новичок который до этого ни разу не писал на Elixir ничего сложнее HelloWorld, т.е. я, начинает приносить пользу буквально через неделю, что по моему опыту очень хороший результат. Continue reading

Windows, Linux и шифрованные диски

Я очень долго использовал устройства Apple в качестве основных рабочих систем и дома и на работе, лет 10, наверное. В целом, это удобно, так как доступны как все приложения которые нужны как для хобби (Capture One & Photoshop), так и рабочие инструменты. Ко времени очередного обновления железа я успел не только заинтересоваться машинным обучением и внимательно посмотреть на современный рынок железа, но и упереться в то, что Apple и GPGPU не совместимы в принципе. После всех метаний и попыток запустить то что мне хочется на устройствах от Apple у меня состоялся большой исход к их конкурентам, ведь зачем платить больше и не иметь доступа к GPGPU, если можно его получить за меньшие деньги?

Перебравшись на Windows дома я довольно долго пытался убедить себя в том, что к всему этому ужасу можно привыкнуть и я просто забыл как классно работать в Windows разработчику… но все же нет. Привыкнуть к PowerShell и его окружению вместо Bash и стандартного набора команд POSIX, а так же отсутствию не глючного терминала просто не возможно. И WSL тут никак не спасает, так как подсистема не только ощутимо тормозит на любом I/O, но и довольно глючная, а кривые терминалы еще сильнее сужают возможности её использования в разработке.

Continue reading