RPC Frameworks and Programming Languages

RPC frameworks are something that I never thought I would be particularly interested in… until I joined the bubble, where nearly everything used the same framework, which made RPC frameworks very appealing. But despite both my previous and current employers releasing two similar RPC frameworks (gRPC and Apache Thrift respectively), they are not really that commonly used in Open Source, from what I can tell. D-Bus technically counts, but it’s also a bus messaging system, rather than a simpler point-to-point RPC system.

On the proprietary software side, RPC/IPC frameworks have existed for dozens of years: CORBA was originally specified in 1991, and Microsoft’s COM was released in 1993. Although these are technically object models rather than just RPC frameworks, they fit into the “general aesthetics” of the discussion.

So, what’s the deal with RPC frameworks? Well, in the general sense, I like to represent them as a set of choices already made for you: they select an IDL (Interface Description Language), they provide some code generation tool leveraging the libraries they select, and they decide how structures are encoded on the wire. They are, by their own nature, restrictive rather than flexible. And that’s the good thing.

Because if we considered the most flexible options, we’d be considering IP as an RPC framework, and that’s not good — if all we have is IP, it’s hard for two components developed in isolation to be able to talk together. That’s why we have higher level protocols, and that’s why even just using HTTP as an RPC protocol is not good enough: it doesn’t define anywhere close to the semantics you need to be able to use it as a protocol without knowing both client and server code.

And one of the restrictions that I think RPC frameworks are good for, is making you drop the convention of specific programming languages — or at least of whichever programming language they didn’t take after. Because clearly, various RPC frameworks inspire themselves from different starting languages, and so their conventions feel more or less at ease in each language depending on how far they are from the starting language.

So for instance, if you look at gRPC, errors are returned with a status code and a detailed status structure, while in Thrift you declare specific exception structures that your interfaces can throw. Both options make different compromise, and they require different amount of boilerplate code to feel more at ease with different languages.

There are programming languages, particularly in the functional family (I’m looking at you, Erlang!) that don’t really “do” error checking — if you made a mistake somewhere, you expect that some type of error will be raise/thrown/returned, and everything else will fall behind it. So an RPC convention with a failure state and a (Adam Savage voice) “here’s your problem” long stack trace would fit them perfectly fine.

This would be equivalent of having HTTP only ever return error codes 400 and maybe 500 — client or server error, and that’s about it. You deal with it, after all it’s nearly always a human in front of a computer looking at the error message, no? Well…

Turns out that being specific to a point of what your error messages are can be very useful, particularly when interacting at a distance (either physical distance, or the distance of not knowing the code of whatever you’re talking to) — which is now HTTP 401 is used to trigger an authentication request on most browsers. If you wanted to go a further step, you could consider a 451 response as an automated trigger to re-request the same page from a VPN in a different country (particularly useful with GDPR-restricted news sources in the USA, nowadays).

Personally, I think this is the reason why the dream of thin client libraries, in my experience, stays a dream. While, yes, with a perfectly specified RPC interface definition you could just use the RPC functions as if they were a library themselves… that usually means that the calls don’t “feel” correct for the language, for any language.

Instead, I personally think you need a wrapper library that can expose the RPC interfaces with a language-native approach — think builder paradigms in Java, and context managers in Python. Not doing so leads, in my experience, to either people implementing their own wrapper libraries you have no control over, or pretty bad code overall, because the people knowing the language refuse to touch the unwrapped client.

This is also, increasingly, relevant for local tooling — because honestly I’d rather have an RPC-based interface over Unix Domain Sockets (which allow you to pass authentication information) rather than running command line tools as subprocesses and trying to parse their output. And while for simpler services, signal-based communication or very simple “text” protocols would work just as well, there’s value in having a “lingua franca” to speak between different services.

I guess what I’m saying is that, unlike programming languages, I do think we should make, and stick to, choices on RPC systems. The fact that for the longest time most of Windows apps could share the same basic IPC/RPC system was a significant advantage (nowadays there’s… somewhat confusion at least in my eyes — and that probably has something to do with the amount of localhost-only HTTP servers that are running on my machines).

In the Open Source world, it feels like we don’t really seem to like the idea of taking options away – which was clearly visible when the whole systemd integration started – and that makes choices, and integrations, much harder. Unfortunately, that also means significantly higher cost to integrate components together — and a big blame game when some of the bigger, not-open players decide to make some of those choices (cough application-specific passwords cough).

Programming Languages are Tools

Recently, I shared a long Twitter Thread while I went through a bunch of GitHub repositories that I officially archived — they are still all available of course, but I’m not caring to take issues, or pull request; if you happen to bump into something you would care to use, feel free to fork and use it.

As these projects come from many different “eras” of my career, they all are different in style, license, and programming language used. I already have written before about my current take in licensing, so I don’t think there’s any need for me to go through more of it right now. But I didn’t really spend much time talking about languages on the blog, so I thought I would at least share my point of view with this.

I started programming with BASIC, on C64, GW-BASIC, QBasic, and eventually Visual Basic 5 CCE. I went on to learn C++ for high school, I tried (and failed) learning Pascal. I learnt PHP because that was what seemed cool, and I couldn’t get into Java at first at all. I learnt to appreciate “good old C”, and Shell. I failed at my first two attempts to get into Python, ended up picking up Ruby, and eventually C#. For a job I ended up digging deep into ActionScript (Flash’s programming language), and some proprietary language that was attached to the RDBMS that they had been using. To make my life easier, I learnt Perl (in 2012!) to extend Munin. Then I arrived at the Bubble with no knowledge of Python and was thrown in possibly the most Python-heavy team in Dublin at the time.

I like Python, but I also liked most of the other languages I worked with. But I look at languages like tools, and sometimes one tool is better than another because of the features, sometimes because it’s the one you have the most muscle memory with, and sometimes because it’s the one that is already in front of you.

As I write this post I have not started my next dayjob, and I have no idea what programming language I’ll end up using day to day. It might even be JavaScript, which I have next to no experience with. I’m not going to be picky — as long as it’s not a functional programming language, it’s just going to be a bit of work to get used to a new language: syntax, mindset, frameworks, libraries, … Not a walk in the park, but I find it part and parcel of a job.

And because I have changed my “dayjob language” a few times in the past few years, the language that my FLOSS projects were written in tended to change in sync — because that would be the language I’d have the freshest memory in. As I said, I used a ton of Python in the Bubble, and that’s why you can see me releasing a ton of Python projects now. If I had to write something in Ruby, I would have to go back and figure out how much the language changed, and what the current best practices are — this is also true for all of my old projects: if I were to pick up ruby-elf again today, it would probably take me a few days even just to figure out how Ruby evolved since seven years ago (the last time I touched it). If the project didn’t have as much code and test and complexities built in, it would probably be faster to just rewrite the whole thing into a language I have more confidence in. Which probably shows why there’s so much stuff I abandoned over the years.

Of course there are developers who focus on one language and know all of its inside out. They are the ones that make languages possible. But that is not what I do, it’s not my dayjob, and I am much more likely to just accept what the status quo for a project is, and adapt. I can only think once of having rewritten something because of its language — and ironically, that was rewriting Python in Perl. Although in that case, I think the problem was less about the language (despite my ranting back then — little did I know that a few months later I would be forced to learn Python), and more about the language providing a flat battery.

I find that the same is true from a company/employer/project owner point of view — languages are tools. And that means sometimes you stick to the tools you have, and not “buy” more tools (in the form of hiring experts or spending on training), while sometimes you don’t care which tools are used in the shop as long as the results are valid. When you have to build some software that a team has to maintain, for instance, you may not want to introduce a new language just because it’s the preferred language of one of the programmers — even if the language is perfectly fine by itself, you need to consider the cost of relying on a single person being the “expert” in that language.

As a counterexample — before the bubble I was working for a small company (not quite a startup, particularly because it didn’t have any venture capital pouring in, so it did spare expenses). The new product was being developed using the same proprietary language used for a previous breadwinner product. The language was clunky, modelled after early PHP, and hard to wire in with modern HTML, let alone JavaScript (this was 2012). Getting anyone to work on that product would require a significant amount of training, which is why the company owner was the one doing nearly all of the work on it (I did the minimum possible on it myself). Replacing the language would have required re-training the owner to some new language (since they didn’t really know any of the possible alternative languages) but since this was 2012, I kept arguing that it would be significantly cheaper to hire one or two junior developers to reimplement the web side of the product in Rails, leaving the owner working on the Flash application instead — particularly because at the time there wasn’t really much of a web part of the product and reimplementing it would have costed nearly nothing by comparison.

This does not mean that developing new languages is not useful or important. Or that the differences between languages don’t matter. I really enjoy writing Python — but if I needed to write something that is very high performance I wouldn’t go and use Python for it. I’m enjoying CircuitPython as it makes quick prototyping awesome, but I also understand it’s a limitation, as it needs more expensive components to run, and if every cent of your BOM counts, it might be a bad choice.

I also am holding hopes for Rust to become useful as a “base system language” to replace good old C in a bunch of places — if nothing else because removing entire classes of mistakes would be nice. But that is also a compromise: it might introduce new classes of mistakes, and it will have side effects on bootstrapping new architectures. I never expect any migration not to have any costs.

Between Mono and Java

Some time ago I expressed my feelings about C# ; to sum them up, I think it’s a nice language, by itself. It’s near enough C to be understandable by most developers who ever worked with that or C++ and it’s much saner than C++ in my opinion.

But I haven’t said much about Mono, even though I’ve been running GNOME for a while now and of course I’ve been using F-spot and, as Tante suggested, gnome-do.

I’ve been thinking about writing something about this since he also posted about Mono, but I think today is the best day of all, as there has been some interesting news in Java land.

While I do see that Mono has improved hugely since I last tried it (for Beagle), I do still have some reserves against Mono/.NET when compared with Java.

The reason for this is not that I think Mono cannot improve or that Java is technically superior, it’s more that I’m glad Sun finally covered the Java crap. OpenJDK was a very good step further, as it opened most of the important parts of the source code for others. But it also became more interesting in the last few days.

First, Sun accepted the FreeBSD port of their JDK into OpenJDK (which is a very good thing for the Gentoo/FreeBSD project!), and then a Darwin port was merged in OpenJDK. Lovely, Sun is taking the right steps to come out of the crap)

In particular, the porters project is something I would have liked to get involved in, if it wasn’t for last year’s health disaster.

In general, I think Java has now much more chances to become the true high-level multiplatform language and environment, over C# and Mono. This because the main implementation is open, rather than having one (or more) open implementations trying to track down the first and main implementation.

But I’d be seriously interested on a C# compiler that didn’t need Mono runtime, kinda like Vala.