-
-
Notifications
You must be signed in to change notification settings - Fork 78
Description
While #372 tries to attack the problem of build times with the obvious "make it possible to compile less stuff," this issue promotes a slightly more radical approach. Namely, it is well known that splitting a crate up into multiple crates will help with compile times, presumably because it helps make more effective use of multiple CPUs.
I did the same thing with regex. Although, I didn't do it because of build times. I did it because I wanted to expose lower level primitives outside of a standard regex API (memchr and aho-corasick being examples of that) and I wanted to expose some of the regex engine internals for more flexible use cases (that's regex-syntax and regex-automata). I'd guess that this ended up helping build times, although there's no real comparison point there.
My problem with doing this is: 1) it expands the dependency tree and 2) it introduces more semver boundaries. I am loathe to have more semver boundaries. They are a huge fucking pain in the ass. They make releases and refactoring more annoying. Moreover, you have to be super careful about introducing public dependencies, or else the semver API evolution of one crate is tied to another.
With that said... Jiff doesn't actually have a ton of internal code. It has some. But most of what I'd put into separate crates are already in Jiff's public API.
Anyway, here's what I'm thinking:
jiff-primitivewould haveTimestamp,SignedDuration,tz::Offset,civil::{DateTime, Date, Time, ISOWeekDate, Weekday}and maybe some others that I'm thinking about.jiff-tzwould have the TZif and POSIX time zone parsers. It would have a public dependency onjiff-primitive. ProbablyTimeZonehas to live here too.jiff-fmthas what's injiff::fmttoday, and would have public dependencies onjiff-primitiveandjiff-tz.jiffglues everything together. It still has some non-trivial code in it, likeZonedandSpan. Along withTimeZoneDatabase. But otherwise is a facade over the aforementioned crates. Users of Jiff (like users ofregex) wouldn't need to worry about the crates above. Jiff's API would stay the same cohesive self. Annoyingly, Jiff would not have public dependencies on any of the above crates.
From my perspective, there are significant challenges with the above blueprint.
- All 4 crates, as they exist inside of Jiff today, would want to use ranged integers. So I would need to make a new
jiff-rangedor something that exposes the ranged integer types. I really do not like this idea because the ranged integers are a giant fucking mess IMO. So it's more likely that in order for the above to work, I'd want to rip ranged integers out of Jiff. (This raises the question of what I'd do instead, because we still need to check boundary conditions everywhere.) - The API surface of these crates is huge, and the thought of writing docs for them and then copying most of those docs verbatim into Jiff proper, and then writing all of the facade wrappers is just... well... it's terrifying. So in order to make this work, I'd probably have to at least settle for the
jiff-{primitive,tz,fmt}crates to have light or almost no docs. Basically they'd be "internal only, not intended for public use." - Changes at the bottom of the crate dependency graph that are needed higher up require an annoying about Cargo interaction: 1) bump version, 2) release, 3) bump version in dependent, 4) release, 5) repeat. I have to do this in ripgrep and it's awful. It's part of the reason why I don't do ripgrep releases often.
This could be done in a piecemeal fashion to get a sense of whether it's worth doing. I think the first thing here would be to start ripping ranged integers out, probably starting with the civil types. Then the other primitives. Once those are free of ranged integers, we could in theory move them out into a separate crate.