This commit is contained in:
235
content/posts/2024-07-12-building-rust-services.md
Normal file
235
content/posts/2024-07-12-building-rust-services.md
Normal file
@@ -0,0 +1,235 @@
|
||||
---
|
||||
type: blog-post
|
||||
title: Use Rust for Web services
|
||||
description: Rust is becoming a mature language, it is excellent at systems development. In this post I will explain why I think Rust is a viable choice for web services, and in the following posts build a simple web service to showcase all the required features I expect from a language.
|
||||
draft: true
|
||||
date: 2024-07-12
|
||||
updates:
|
||||
- time: 2024-07-12
|
||||
description: first iteration
|
||||
tags:
|
||||
- "#blog"
|
||||
---
|
||||
|
||||
Rust is increasingly becoming more mature, especially for systems development.
|
||||
Rust, however, can also be an excellent choice for building web services, which
|
||||
may not be obvious from the outset, in this post I'll show that Rust is a viable
|
||||
choice and does lend itself to web service development.
|
||||
|
||||
## But, systems development
|
||||
|
||||
It is commonly said that Rust is built for systems development. That is, the
|
||||
core, nitty gritty details of our software stacks. That is definitely also where
|
||||
Rust shines, especially because Rusts values aligns quite well with systems
|
||||
development. That doesn't mean that Rust is unsuited for development in other
|
||||
areas than systems development. It means that we should carefully align what
|
||||
values Rust chooses to prioritize against our own.
|
||||
|
||||
Rust mentions on their website that their primary values are:
|
||||
|
||||
- Performance
|
||||
- Reliability
|
||||
- Productivity
|
||||
|
||||
If that fits your needs then I don't see any reason why you wouldn't choose Rust
|
||||
for building your business logic and serving requests.
|
||||
|
||||
System development typically favors:
|
||||
|
||||
- Performance
|
||||
- Control
|
||||
- Reliability
|
||||
- Security
|
||||
|
||||
Which is why Rust is such an excellent choice for building core infrastructure.
|
||||
|
||||
Building web services is entirely up to you however, most companies I've seen
|
||||
favors:
|
||||
|
||||
- Productivity
|
||||
- Reliability
|
||||
- Catalog of libraries
|
||||
- Ease of use
|
||||
|
||||
I won't go into each an every items for systems development, but I do think it
|
||||
is important to reason about why those 3 values are the ones I've chosen to
|
||||
select for:
|
||||
|
||||
### Productivity
|
||||
|
||||
As software developers building business services, we're expected to deliver
|
||||
results. Software engineers working on front facing services, are often valued
|
||||
on how fast they can deliver results. It is simply how the business and
|
||||
technology work together. Business wants results, tech wants reliable, modern
|
||||
and understandable systems. Rust does select for productivity, but not in the
|
||||
way we usually think about it, when Rust says productivity, they say, we've got
|
||||
great tooling, a robust build system, package manager, auto-formatter etc. Built
|
||||
right into the tool you use the most when developing Rust. Rust does have it
|
||||
downsides for productivity, it has a rigorous type system, which requires fairly
|
||||
long compile times.
|
||||
|
||||
This is just my experience with Rust speaking here, but Rust really means
|
||||
productivity in the long run, delayed gratification if you will. It will
|
||||
sometimes feel like easy things are hard, but also that hard things are easy. In
|
||||
my experience with Rust, building web services from scratch can take quite a
|
||||
while, but doing refactorings later on, is fairly easy and reliable, simply
|
||||
because Rust is so rigorous. There is often the feeling that if it compiles it
|
||||
works, even if tests aren't run. Much more so than in other languages with
|
||||
weaker guarantees in its type system.
|
||||
|
||||
### Reliability
|
||||
|
||||
Software engineers want to depend on tools that just works, they don't want
|
||||
crazy crashes, memory overflows, garbage collectors running amok, shared memory
|
||||
etc. Nobody wants to be paged at night, or spend weeks sorting out invalid data
|
||||
because a software service was misused, or data transformed in the wrong way.
|
||||
|
||||
Rust leans into reliability hard, it chooses reliability over nearly all other
|
||||
values. When Rust runs, it runs well, consistently, and fast. I've had services
|
||||
in production seeing traffic in all sorts of conditions and the programs keeps
|
||||
chucking along, some that have been running for months, just sitting there doing
|
||||
their thing.
|
||||
|
||||
### Catalog of libraries
|
||||
|
||||
Software engineers working on business services are often integrating with a
|
||||
variety of systems. The language they choose either needs an incredibly vast set
|
||||
of libraries, and/or be excellent at interopating with other libraries. Hey, I
|
||||
need this program to send files to an SFTP server, I need a connection with this
|
||||
server, but it needs to be over mTLS, I've got these massive XML files that
|
||||
needs to be parsed. We've got this arcane library that we need for reason,
|
||||
please make sure to call it in this way.
|
||||
|
||||
There are tons of requirements for libraries, for common things such as logging,
|
||||
metrics, web apis, message protocols, message queues etc. Rust in my opinion
|
||||
sits at the early adopter/initial mainstream cycle here. While Rust has an
|
||||
incredibly vast library of tools, a lot of these tools are built by hobbyists,
|
||||
excellent quality don't get me wrong. But has currently is a second class
|
||||
citizen for a lot of providers. We are beginning to see services provide SDKs
|
||||
and so on for Rust, but often in the 2nd or 3rd wave of tools. Languages likes
|
||||
Javascript, Python, Golang and so on are still a little ahead.
|
||||
|
||||
### Ease of use
|
||||
|
||||
As software engineers working on business services, we mostly spend our time on
|
||||
domain logic, talking to the business, architecting the apis for our customers,
|
||||
figuring out the right data paths for models, integrating with other internal
|
||||
customers. As such we want a simple language that should be powerful, but easy
|
||||
to use, we don't want to skimp on features, but we generally favor simplicity.
|
||||
|
||||
Rust does clash here, for one thing, it isn't easy to learn. Rust kind of has
|
||||
this unfortunate curve of learning, where initially you are fighting with the
|
||||
compiler to get your program built. As you lean more into the standard library
|
||||
you learn some bad practices, and use features to solve the short comings in
|
||||
dealing with Rust. Until hopefully when you've grokked Rusts model, can actually
|
||||
build quite simple software, that isn't overly verbose, or complex to use.
|
||||
|
||||
This sadly puts simplicity together with mastery of the language. While not
|
||||
impossible, it does make it difficult for a beginner/intermediate Rust developer
|
||||
to build simple, concise Rust code. Unlike, Golang, Python and so on. Which puts
|
||||
simplicity forward as some of their values. Golang especially makes it easy for
|
||||
even beginners of the language, to produce ideomatic, simple code.
|
||||
|
||||
## Pretty decent overlap for web services
|
||||
|
||||
Rust actually seems from the outset to be pretty well suited for web service
|
||||
development. It is ergonomic, favors productivity, rock solid to use, and has a
|
||||
vast array of libraries. From experience Rust do have some issues for web
|
||||
service development. Primarily that isn't easy to pick up. This is a point I
|
||||
hope to help alleviate in this article. In my opinion a lot of the current use
|
||||
cases showing Rust, are in too technically complex domains, making it not as
|
||||
clear that Rust can actually produce quite simple code, for simple technical
|
||||
problems. Instead focusing on showing Rusts power in handling complex technical
|
||||
situations, especially with illustrating that Rust can handle these situations
|
||||
with effective code. Effective code is not equal simple code. And while from the
|
||||
outset, concise effective code looks simple. It often isn't, it requires deep
|
||||
knowledge of the language to operate, something we'd like to avoid unless the
|
||||
situation warrants it.
|
||||
|
||||
With this series, I hope to show some programming patterns for building
|
||||
services, which should hopefully put you in the right path. Some of the code
|
||||
will look trivial for the most part, as we're at the end of the day handling
|
||||
quite simple technical problems. It should, however, show that Rust while
|
||||
capable and often show wielding a sledgehammer, often it isn't warranted or the
|
||||
thing to use.
|
||||
|
||||
## Why Rust is hard to learn
|
||||
|
||||
Rust is currently in this situation where it has a reputation for being hard to
|
||||
learn, which I'd sort of tend to agree with, as I struggled to learn it myself.
|
||||
I do have a single reason as to why Rust was hard to learn for me. I didn't have
|
||||
a peer to discuss problems with, no mentor for guiding me on the right path,
|
||||
etc. Lack of solid examples for doing the kind of programming I am interested in
|
||||
(primarily web service development and developer tooling).
|
||||
|
||||
I myself had to go the entire cycle, thorns and all, from beginner and
|
||||
intermediate in Rust, to having a somewhat good grasp on the language and
|
||||
ecosystem to be effective in the language. In a prior post, I showed how to
|
||||
build services in Rust as well. If I'd had some of these kinds of guides earlier
|
||||
then I'd certainly be much further in being productive in Rust.
|
||||
|
||||
## Rust is obsessed with itself
|
||||
|
||||
The Rust community is crazy with the language, it sometimes feels a little bit
|
||||
like a cult. In the last year or so, I have felt it subside a little bit, at
|
||||
least in my local community. But in general, we're obsessed with the language,
|
||||
nerding out writing blog posts going into hairy problems, finding arcane tricks
|
||||
to solve the issue. Crazy debugging techniques to figure out why a certain
|
||||
program spent a few more cycles than it should on a given function call, etc.
|
||||
While entertaining, I do think we need to take a step back, and look at Rust for
|
||||
the values it brings, and see how we can promote in a more digestible format. So
|
||||
that the more casual side of Rust is shown as a contrast to the expert blogs.
|
||||
|
||||
## Rust should be better at putting you in the pit of success
|
||||
|
||||
To succeed with Rust you need to be in the pit of success. You can't be rolling
|
||||
around at the sides like you can in most other languages. In Rust, if you're not
|
||||
following the rules, you will come to refactor your app, many times. Do tricks
|
||||
to avoid ownership rules, all tricks, that compound in the apps being more
|
||||
complex than required.
|
||||
|
||||
Rust has tons of rules of how you should write the language, it just doens't
|
||||
enforce them that strictly. Some are shown explicitly and implicitly in various
|
||||
material, The Rust Book etc. However, most of these rules, really have to be
|
||||
enforced to actually be followed. Most languages have implicit rules that you
|
||||
need to follow. C++ has a really long book about it, Rust has The Book etc.
|
||||
Golang also has implicit requirements. For examples that consumers should close
|
||||
channels, functions should return structs but accept interfaces etc. Golang
|
||||
feels like a language where it is easy to fall into the pit of success. Mostly
|
||||
because the language is fairly easy to understand, so it becomes easier to grasp
|
||||
the implicit rules.
|
||||
|
||||
Rust is hard to learn, as you are given a solution to the problem you face at
|
||||
hand. But that solution is nearly always a sledge hammer instead of what you
|
||||
should be doing, which is be consistent. Which is super difficult to do when you
|
||||
don't know what the right thing to do is.
|
||||
|
||||
The learning curve for a Rust problem goes somewhat like this:
|
||||
|
||||
1. User creates module for handling user state
|
||||
2. User gets compile error because memory is shared between threads.
|
||||
3. User reads up on what tools are available to solve the problem
|
||||
4. User uses an Arc<Mutex<...>> to share memory, and lock access so that nobody
|
||||
can touch the data while someone is modifying it
|
||||
5. User exposes their services under Arc<Mutex<>> everywhere. Every class now
|
||||
need to take an Arc Mutex as input for one or more dependencies. Database,
|
||||
Logger, Business service. The code becomes a mess to maintain and refactor.
|
||||
6. User refactors the app, they find out that they should wrap state into
|
||||
consistent ideomatic modules that only expose the api requires. All thread
|
||||
safety mechanisms should either be exposed as APIs or maintained internally.
|
||||
7. The user has to refactor the entire app at once to make the migration
|
||||
successful
|
||||
8. The user can now share dependencies with references, the code is fairly
|
||||
concise, testable.
|
||||
|
||||
Rust has tons of rules, hidden in various syntax rules, that while boiling down
|
||||
to simple concepts are difficult to implement and understand, some of the rules
|
||||
even depend on the context they're in and which values your program selects for.
|
||||
If writing web services, you'll want to build modules that encapsulate their
|
||||
state and provides a shareable reference outside, that is cloneable and safe to
|
||||
use. Structs are preferred over traits as the API. We'll go into each of these
|
||||
in a more thorough manner in later posts as we encounter the need for them. If
|
||||
you're a Rust developer you may contest to these ideas, which obviously is fine,
|
||||
as I may be wrong :,). However, I hope that the series will show how I
|
||||
consistently build services in Rust, that are ergonomic to write and not overly
|
||||
verbose.
|
Reference in New Issue
Block a user