On Friday, 18 March 2022 at 18:16:51 UTC, Ali Çehreli wrote:
As a long-time part of the D community, I am ashamed to admit that I don't use dub. I am ashamed because there is no particular reason, or my reasons may not be rational.


dub is legitimately awful. I only use it when forced to, and even making my libs available for others to use through it is quite an unnecessary hassle due to its bad design.

That sounds great but aren't there common needs of those modules to share code from common modules?

Some. My policy is:

1) Factor out shared things when I *must*, not just because I can.

So if it just coincidentally happens to be the same code, I'd actually rather copy/paste it than import it. Having a private copy can be a hassle - if a bug fix applies to both, I need to copy/paste it again - but it also has two major benefits: it keeps the build simple and it keeps the private implementation actually private. This means I'm not tempted to complicate the interface to support two slightly different use cases if the need arises; I have the freedom to edit it to customize for one use without worrying about breaking it for another.

When I must factor out it is usually because it is part of a shared public *interface* rather than an implementation detail. A shared interface happens when interoperability is required. The biggest example in my libs is the Color and MemoryImage objects, which are loaded from independent image format modules and then can be loaded into independent screen drawing or editing modules. Loading an image then being unable to display it without a type conversion* would be a bit silly, hence the shared type.

* Of course, sometimes you convert anyway. With .tupleof or getAsBytes or something, you can do agnostic conversions, but it is sometimes nice to just have `class SpecialImage { this(GenericImage) { } }` to do the conversions and that's where a shared third module comes in, so they can both `import genericimage;`.

2) Once I do decide to share something, there's a policy of tiers:

first tier has zero imports (exceptions made for druntime and SOMETIMES phobos, but i've been strict about phobos lately too). These try to be the majority of them, providing interop components and some encapsulated basic functionality. They can import other things but only if the user actually uses it. For example, dom.d has zero imports for basic functions. But if you ask it to load a non-utf8 file, or a file from the web, it will import arsd.characterencodings and/or arsd.http2 on-demand.

Basic functionality must just work, it allows those opt-in extensions though.

second tier has generally just one import, and it must be from the first tier or maybe a common C library. I make some exceptions to add an interop interface module too, but I really try to keep it to just one. These built on the interop components to provide some advanced functionality. This is where my `script.d` comes in, for example, extending `jsvar.d`'s basic functionality with a dynamic script capability.

I also consider `minigui.d` to be here, since it extends simpledisplay's basic drawing with a higher-level representation of widgets and controls, though since simpledisplay itself imports color.d now (it didn't when I first wrote them... making that change was something I really didn't want to do, but was forced to by practical considerations), minigui does have two imports... but still, I'm leaving it there.

Then, finally, there's the third tier, which I call the application/framework tier, which is the rarest one in my released code (but most common in my proprietary code, where I just `dmd -i` it and use whatever is convenient). At this point, I'll actually pull in whatever I want (from the arsd package, that is) so there is no limit on the number of imports. I still tend to minimize them, but won't take extraordinary effort. This is quite rare for me to do in a library module since this locks it out of use by any other library module! Obviously, no tier one or two files can import a tier three, so if I want to actually reuse anything in there, it must be factored out back to independence first.

C libraries btw are themselves also imports, so I also minimize them, but there's again some grey area: postgres.d use both database.d as the shared interface, but libpq as its implementation. I still consider it tier two though, despite a C library being even harder for the user to set up than 50 arsd modules.

3) I try to minimize and batch breaking changes, including breaks to the build instructions. When I changed simpledisplay to import color, it kinda bugged me since for a few years at that point, I told people they can just download it off my website and go.

I AM considering changing this policy slightly and moving more to tier two, so it is the majority instead of tier one. All my new instructions say "dmd -i" instead of "download the file" but I still am really iffy on if it is worth it. Merging the Uri structs and the event loops sounds nice, having the containers and exception helpers factored out would bring some real benefits, but I've had this zero-or-one import policy for so long, making it one-or-two seems too far. But I'll decide next year when the next breaking change release is scheduled.

(btw my last breaking change release was last summer and it actually broke almost nothing since i was able to migration path it to considerable joy. im guessing most my users never even noticed it happened)

Despite such risks many projects just pull in code. (?) What am I missing?

It is amazing how many pretty silly things are accepted as gospel.

Reply via email to