no interest in making that style of coding easier. I want users to consciously choose what config they're using. I view blindly picking a default as a mistake […]”
are: • how successful all users are in using the API • the quality of the output that users achieve by using the API • the percentage of users making the correct choices
y blind • The only metrics we have is download stats, which mostly correlate with CI setups, and not true utilization • User frustration is often the only other form of feedback we get • We need extrapolation from user surveys and interviews • In the absence of this, personal frustration and issues is a good proxy
Defaults: easy to get started, trivial to stay on the golden path as it changes • Small Surface Area: enable room to breath and innovate, without breaking users • Backwards compatible: avoid unnecessary churn to keep users on the golden path
build • That path might change over time • Change requires adjustment by users • Fast change means users being left behind • Measuring success: users on the golden path (not churning, not staying on old versions, not hating the upgrade experience, not using old patterns)
and of two types: • Absolute defaults that cannot be changed (i32::default() -> 0) • Defaults that allow a level of fl exibility (Default Hasher: SipHash) • For defaults to allow fl exibility, care has to be taken: • Set rules and expectations about stability • Aim for some level of change
be non portable • Hasher is documented to change • No expectation around cross-version/process stability • A better hasher can be picked, all code ever written bene fi ts at once
of a protocol? • If you have an API that drives a protocol, consider that protocol to consider defaults • This approach can only be guidance, a lot of situations do not allow it.
All those di ff erent types create duplicated generated code • Example: isolate conversions and call into shared functions to reduce the total amount of copied code.
are Layered Like Onions • Only provide the outermost layer fi rst • Keeps the inner layers fl exibility to change • Over time, consider exposing internal layers under separate stability guarantees
for utilities • For instance "insta" has utility functions and types that are rarely useful. The ones I subscribe stability to are re-exported under a speci fi c module.
public, but you don't want anyone to use it. • Common example: utility functionality for macros. • Here both __context and __context_pair! are public but hidden
I know abstraction over implementations is necessary • Did you notice that BTreeMap and HashMap are not expressed via traits? • The usefulness of abstraction even for interchangeable types is sometimes unclear • You can always add traits later
• It also gives it the `.to_string()` method • Certain types need it in the contract (eg: all errors) • Recommendation: avoid in most cases unless you implement a custom integer, string etc.
• Neither can be universally provided • Clone: really useful, consider adding • If you ever feel you need to take it away, consider Arc<T> internally • Copy: might inhibit future change, but really useful • Some types regrettably do not have Copy (eg: Range) and people hate it
Errors deserve attention just as much as your other types • A talk all by itself, so here the basics: • Implement std::error::Error on your errors • Implement source() if you think someone might want to peak into