Programming an Exchange: Why We Choose Rust?
The programming language Rust is gradually gaining popularity not only among developers but also within the management community. Such growing popularity is not unfounded, especially in the case of technologies with a high entry barrier. In this article, I want to analyse why Rust has earned such acclaim, using the example of Best Architects, the company where I work, where Rust is the primary development language, and one of our key products — the exchange.
When choosing any development tool, the choice of language is driven by the requirements posed to the product under development. What requirements does an exchange have?
Firstly, Reliability
Reliability is crucial. No one wants to trade on an exchange that might lose their money due to software errors. Unfortunately, there is no 100% foolproof way to avoid bugs; there’s only a chance to reduce their probability. Testing has proven itself in this regard, but even it does not provide absolute guarantees; it merely decreases the likelihood of issues. Even if it seems that the software is entirely covered by tests at all levels, it’s only an illusion; tests themselves can contain errors and may overlook certain usage scenarios. In addition to testing, static analysis tools are beneficial. They allow detecting issues at the development stage.
There is no 100% foolproof way to avoid bugs. There’s only a chance to reduce their probability.
An ideal language, in terms of reliability, aims for the “compiles means it works” paradigm. Although no language is perfect, some stand out significantly from the majority of languages built on the opposite paradigm of “it works means it compiles.”
Secondly, Performance
Performance is crucial. An exchange must execute transactions as quickly as possible. On our exchange, we aim for transaction times in the range of tens, at worst, hundreds of microseconds. The significance of this parameter is evident when considering that most brokers place their servers as close as possible to exchanges to minimise network delays. Another performance factor vital for an exchange is the ability to withstand high loads. We live in the era of algorithmic trading, where a significant portion of the market involves programs executing a vast number of transactions every second. A trader using traditional methods simply cannot compete with such programs, and the prevalence of these programs is only increasing. Add copy trading, offered today by many brokers, where one trader’s transactions instantly lead to similar transactions by those following them.
Performance is crucial. An exchange must execute transactions as quickly as possible.
An exchange must handle millions of transactions per second without losing speed in their execution. Although the choice of language is not the only factor affecting performance, it matters for such high requirements. Languages with runtime interpretation, JIT compilation, and/or garbage collection have less predictable performance, most likely worse than languages with a more minimalist runtime. Therefore, for higher performance, a language with manual memory management and AOT compilation to machine code is preferable.
Thirdly, Development Speed
Development speed is essential. This requirement is crucial for almost any modern product, not just an exchange. The faster development can deliver new features, the more competitive the product becomes among its counterparts. Development speed is influenced by various factors that differ among different languages: the presence of high-level abstractions, a developed ecosystem of libraries with ready solutions for typical problems, advanced development and debugging tools, etc. It’s good if the language allows convenient work with multithreaded and asynchronous computations. If we are aiming for performance, it’s highly desirable for abstractions to cost as little as possible, preferably being zero-cost. And if we want to manage memory manually, it’s good to have smart pointers and collections that will manage memory allocation and deallocation for us.
Combine all these requirements, and the choice of a programming language narrows down to a very shortlist. With just the performance requirement, you can choose between C++ and Rust, maybe a couple of other languages that will quickly be discarded either during speed of development analysis or when trying to hire someone. Add reliability, and Rust significantly outpaces C++ simply by the fact that it guarantees no undefined behaviour unless you write the ‘unsafe’ keyword.
Many IT businesses are hesitant to adopt Rust, preferring to build their services in Node.js, Python, or Go. Some fear that they won’t be able to hire anyone, and training existing developers will be extremely challenging due to the steep learning curve. However, Rust developers exist; many know several other languages and would prefer Rust if given the choice. Some fear that it’s just a trendy toy. However, Rust has been developing for about as long as Node.js or Go; it’s ready for production, and probably something you use daily is written in Rust.
Some view Rust purely as a language for systems development. Although its ecosystem has libraries for almost any database, almost any message broker, and almost any serialisation format (many working through the unified interface of the serde library). And writing a REST API using axum, rocket, or actix-web is no more challenging than using Django, Flask, Express, or Koa.
In conclusion, I want to say that Rust developers sleep more soundly, burn out less often, and resign less frequently. The reason for this is fewer bugs, an almost never-crashing product, and a language that is pleasant to work with.