Re: Bloat

skyjake says software bloat is a relative term.

99 KLOC (53 KLOC without libraries) is relatively ... a lot, for a Gemini client. I think "bloat" is a bad thing in this context, because:

It's not easy to write a small client, though, and it's not easy to write a useful client with good user experience. If a dependency is small, feels "native" enough and has a smaller memory/CPU footprint compared to the native option, I tend to agree with this Go proverb:

A little copying is better than a little dependency.

Go Proverbs

This is why gplaces uses vendored bestline instead of a shared readline. The latter leaks memory and the "hints" feature of bestline is very useful. In addition, gplaces uses a 3.x-compatible and LibreSSL-compatible subset of the OpenSSL 1.x API, without any abstraction layer, and uses curl's API parser. These two libraries are very common, and all my attempts to wrap their API only made gplaces less readable.

Most of gplaces.c (0.16.8, 1032 LOC) is glue code, and the "big" features are pretty small in terms of LOC. After some crude CTRL+x, CTRL+n and CTRL+v work:

Feature                       | LOC
------------------------------+-----
Status line handling          | 159
TOFU                          | 30
Client certificate generation | 37
Parsing and pretty-printing   | 100
bestline.c                    | 3057

gplaces shows that it *is* possible to write a Gemini client, with the basics plus some utilities like certificate generation, in a tiny codebase. This makes me wonder if a big portion of the Lagrange codebase consists of functions like these two:

void initCurrent_Time(iTime *d) {
    clock_gettime(CLOCK_REALTIME, &d->ts);
}

time.c, line 59

iBool isExpired_TlsCertificate(const iTlsCertificate *d) {
    if (!d->cert) return iTrue;
    return X509_cmp_current_time(X509_get0_notAfter(d->cert)) < 0;
}

tlsrequest.c, line 545

I don't think these functions make the code easier to understand: they hide implementation details ("time" means "real time" or a monotonic timer?) and I'm used to the libc and OpenSSL API. The "let's wrap everything" attitude can be a problem if it makes life harder for contributors and increase binary size (because these one-liners are not inlined). Is Lagrange bloated, then? I don't think it's easy to write a graphical client with the same feature set and consistent UI across multiple platforms. This coding style thing doesn't make a huge difference. I do prefer a compact, tight codebase, because this makes it easier to debug and generally, easier to work with, even if the binary size doesn't change much.

Let's not forget that gplaces is super bloated, too: the static executable is only slightly smaller than Bombadillo, and the latter (a much better client, for most users), is written in Go. We're all in the same boat here!

A basic but usable (not ultra-spartan) client should fit comfortably within 50 or so lines of code in a modern high-level language. Certainly not more than 100.

FAQ