Dawid Ciężarkiewicz aka `dpc`

contrarian notes on software engineering, Open Source hacking, cryptocurrencies etc.

The new era is beginning, and the old era is ending. C and C++ as a lingua franca of systems programming are being displaced by Rust. Many will deny it, many will fight it. But I'm confident it is already happening and is inevitable to continue at accelerating pace.


As some people might now I am a vocal OOP critic. I think it is fair to say that I am on a crusade, actually. :D

Oftentimes, my long online posts explaining what is wrong with OOP meet with a No true Scotsman argument. That I am somehow pointing out to flaws in caricature of an OOP, and the correct OOP is free from these issues. To prove to myself and other people that it is not the case, I decided to go through some classic OOP books, and criticize the OOP examples in them.

My first choice is the Clean Architecture by Robert C. Martin (aka Uncle Bob). I must admit Uncle Bob is not one of my favorite software engineering gurus. But he is a reputable and experience developer, and if he was to write a caricature of OOP, then who are the people who dare to say they do it right?


I am running my #Urbit ship on Digital Ocean using #NixOS .

Took me quite a bit of time to figure the actual settings to use for Nginx to forward HTTPS to the port 8080 that vere uses. For some reason, default settings were causing the whole UI to misbehave completely: keep showing nonsense, disconnect etc. I finally found a working setup by asking around, googling and just trail and error.

In case you're interested, here are the settings that worked for me. TLS is set up using Let's Encrypt, terminated in Nginx, HTTP is redirected to HTTPs and HTTPs goes to vere.

    services.nginx.enable = true;
    services.nginx.recommendedOptimisation = true;
    services.nginx.recommendedProxySettings = true;
    services.nginx.recommendedGzipSettings = true;
    services.nginx.recommendedTlsSettings = true;

    services.nginx.virtualHosts."napzod-dopzod.arvo.network" = {
        forceSSL = true;
        enableACME = true;
        http2 = false;
        locations."/" = {
            proxyWebsockets = true;
            proxyPass = "";
            extraConfig = ''
              # required when the target is also TLS server with multiple hosts
              proxy_ssl_server_name on;
              # required when the server wants to use HTTP Authentication
              proxy_pass_header Authorization;
              chunked_transfer_encoding off; 
              proxy_buffering off; 
              proxy_cache off; 
            '' + "proxy_set_header Connection '';"; 

    security.acme.certs = {
      "napzod-dopzod.arvo.network".email = "myemail@example.com";

I have not attempted to minimize these settings, so I don't know which ones are actually necessary.

I am still running vere in a lame way: by starting it in tmux session, since I don't have a working Nix recipe for it yet. If you do, make sure to submit a PR to Nixpkgs so we can all benefit.

Here is my take on the relationship between functional, imperative, actor, and service-oriented programming, and a short description of a hybrid approach combining them all and giving each an explicit function and level within the system (computation).

There's a natural progression between them, and a great deal of efficiency when they are combined to do the part they do best. I call it “opportunistic programming” (working title). If you're aware of existing ideas/publications etc. along the same lines, please share them with me. As usually, I probably didn't discover anything new.

Whenever you can – it's best to express computation declaratively: using functional programming. It should be a default mode of operation, only not used when not practical. It has many advantages over alternative approaches, and very little downsides (when used opportunistically). The goal here is to express as much logic as possible using pure mathematical computation that is easy to reason about, prove, and test.

When your code is dealing with external side-effects or things like computation performance are important, you have to abandon the luxury of the FP mode, and switch to imperative code. It's lesser and harder to use mode but it is closer to how the reality (computers) works, so it gives more control. You should still aim at writing as much as possible in FP mode, and only wrap the FP core logic in an imperative shell coordinating data-sharing, mutation and side-effects where needed. Depending on the problem and requirements the ratio might be different, but generally, imperative code should be isolated and kept to the necessary minimum. The goal here is to either explain to the machine exactly how to efficiently compute something and/or take control of ordering between events.

As your computation (program) grows it will become apparent it is possible to split it into parts that don't require “data-coherency”. That means – parts that have no reason to share data (even for performance) and it is natural for them to communicate entirely using message passing (immutable copies of data), typically using in-memory message queues of some kind. That's (kind of) the actor model. That goal here is to encapsulate and decompose the system along the most natural borders. The size of actors depends entirely on the problem. Some programs can be composed of many tiny actors – single function each. Some will be hard to decompose at all or have complex and big (code-wise) actors. It is worthwhile to consciously consider the design possibilities that allow finer granularity in this layer.

When the operational needs (availability, scalability, etc.) demand it, actors from the previous paragraph are a natural candidate to be moved to run on different machines potentially in many copies and become “services”. The cost and additional work are in handling: network latency, unreliable communication, and potential data loss. The goal here is to adapt the computation to the requirements of hardware: limited capacity and imperfect availability.

That's it. Some side-comments:

  1. It's a shame that FP is still not a default school of mainstream programming. FP is really easy and natural when applied opportunistically and generally will lead to both better runtime and developer performance.
  2. My main problem with OOP (and possibly actor model) is granularity. Encapsulation is costly. That's why encapsulating every single “object” is a bad idea. The right granularity for OOP is the module/library/component level, and for actors – along the problem-dependent natural lines where sharing data is no longer required anyway. Within functional and imperative code I recommend data-oriented approach, instead of the typical OOP-approach.
  3. This model easily handles the problem of converting “monolith” into microservices-based system. “encapsulation and decomposition” level is just “microservices but without the extra work (yet)”.

#software #oop

Everybody does code reviews nowadays (I hope!). Research shows that it increases quality... blah, blah, blah.

But, how many times did you think about how much does it cost? Because you know ... Code Review isn't free. Code Review takes time. A developer publishes a PR, and then... what? Is there anyone readily available to review it? Other devs are busy doing their own stuff. Should the developer interrupt someone to get a fast review? Or just context switch to something else for now? I'm sure the research was not investigating this part.

Before most readers close this page enraged, shouting “Obviously, Code Review is good, you fool! It's totally worth it!” Yes, Code Review is worth it! I didn't say it isn't. I only said that it does have productivity cost and you should not ignore it. Please, while you are aware of the benefits you're getting, also acknowledge the costs you made to gain them.

I'm not just trying to be controversial and/or annoying. My point is: if you want to be good at something, you have to be mindful of both of them: benefits and costs, to maximize the former, while minimizing the later! Most of my guidelines below are going to be about minimizing the cost of Code Reviews.


This is going to be a quick overview of how I tend to write my application code. It might be a bit Rust-centric, but I apply similar methods in all programming languages I use.

I think it's an important subject and during past online discussions about learning Rust and writing code “the Rust-way”, I was asked multiple times how do I do it. I don't really have a lot of time to write something longer and better structured, so please excuse anything that is confusing. You get what you pay for.

Also, I don't want to suggest this is some sacred, best way or anything like that. I think this is what I'm typically doing. A result of years of professional and Open Source work and experiences I gained during that time. I'm always happy to learn and get to know other points of view, so I'm happy to hear any feedback.


Or: Why there's no inflation and things are weird.

TL;DR: Global economy is much closer to a game of Monopoly than most people realize. We're at this phase when it's clear who owns all the hotels, and the only thing that allows other players to keep playing is taking on more and more debt. This big Monopoly game always ends up like this, and is being restarted. That's the economic “supercycle”.

All models are wrong, but some are useful

I really enjoy simplifying complex system into useful mental models. This is a short compilation of what and how I think about the macroeconomy. I'm not saying it's right, but it's mine and I wanted to share it. I'll be happy to hear where it is inaccurate or simply wrong. Also, I'm aware that what I say here is probably not very novel.


I really like the Urbit project. I don't know how long I've been following it now. It was definitely earlier than 2016 when I discovered it, probably even 2014.

I think #Urbit is fundamentally trying to address the right set of problems in a comprehensive, holistic way. The fact that the Internet is broken for anything but centralized silos. I am deeply enthusiastic and cheering for Urbit. I do want it to succeed!

But in this post, I'm going to focus on an honest critique. I've actually read a lot of interesting critique of Urbit over the years – all that I could get my hands on. Mine is going to be less ambitious, and mostly down to earth and pragmatic. I hope it won't come out to harsh.


Test runners/frameworks caused me a lot of grief over the years. It's part of the reason why really appreciate how standard #Rust test runner works. As usual with Rust, core developers, and the community at very least avoided the most common misfeatures and made the right thing to be the easiest and most natural thing to do.

That's why i recently (probably too abrasively) bashed an announcement of some new Rust testing library.

I've taken some time and collected a list of feature request, misfeature rants, that I just want to share.