Is it me, or does rust suffer badly from important functionality being contributed rather than baked in by design, leading to, e.g., multiple incompatible ways to do async?
Or am I spoiled by .net, which has an excellent bcl?
Is it me, or does rust suffer badly from important functionality being contributed rather than baked in by design, leading to, e.g., multiple incompatible ways to do async?
Or am I spoiled by .net, which has an excellent bcl?
In straightforward terms, author of the most popular YAML parser (https://lib.rs/crates/serde_yaml) has suddenly given up on it, and marked it as deprecated and unmaintained, without warning or designating another maintainer.
It's not exactly a left-pad situation, because the package remains fully functional (and crates.io doesn't allow deletions), but the package is used in 4000 other crates. Audit and auto-update tools will be complaining about use of an unmaintained crate.
Isn't that author kind of prolific within the rust library world? He didn't do that to any other libraries. Is there something with more info?
Yes.
The release notes for the last release explain: https://github.com/dtolnay/serde-yaml/releases/tag/0.9.34
That is incredibly obnoxious and irresponsible and he should turn over the repo to someone else to manage.
I’m not sure how much open source work you’ve done or what packages you’ve maintained, but in my experience, the “someone else” rarely ever comes. Heck I have even been the someone else and then done a bad job of it!
This is an unrealistic and frankly a bit entitled of a criticism.
This is before we even get to stuff like “it’s not like yaml is a fast moving target, what continued maintenance is really even needed” parts of the equation, or the comment above that said he’s tried already.
The “someone else” also needs to be vetted to ensure that their first update won’t include crypto mining malware.
We should remember that an unmaintained dependency isn’t the worst thing that can happen to a supply chain. There are far worse nightmare scenarios that involve exfiltrated keys, ransomewared files and such.
I’ll bet that if someone with a track record of contributing to the Rust community steps up, he’ll happily transfer control of the crate. But he’s not just going to assign it to some random internet user and put the whole ecosystem at risk.
What's so irresponsible about it? He clearly communicated the state of affairs.
I think he was generous enough with his time for creating the library in the first place and posting this update. It's not like he's owing anything to anybody. I don't understand how you can demand him to do further UNPAID work to recruit maintainers who would also do further unpaid work.
The obnoxious and irresponsible part is the hubris of thinking you can have a package repository system that's going to not have problems like this and left-pad.
You can't. Ever. This is unironically why languages too old for package repositories like C and C++ lend themselves to stabler, higher-quality software.
On top of that is also the entitlement to feel like people providing you software for free MUST ALSO be responsible for polishing it forever. But, hey, I guess that attitude comes naturally with the package repository mindset. ;D People need to do all my work for me!
Anyone else is free to take it! You see that the repository is hosted under his own user. Anyone else can create an organization or host it under their own name.
Are we collectively having Subversion phantom pains or something? Just take it.
he should turn over the repo to someone else to manage
Why can't this "someone else" just fork the repo themselves?
I found an explanation. Reading between the lines I think he found something less abysmal than YAML.
https://github.com/dtolnay/serde-yaml/releases/tag/0.9.34
As of this release, I am not planning to publish further versions of serde_yaml as none of my projects have been using YAML for a long time, so I have archived the GitHub repo and marked the crate deprecated in the version number.
Hm... might still have been better to first ask around if anyone is willing to maintain it instead of marking it as deprecated?
From what I can tell this is a second order effect. No advisory was made against serde-yaml, it was made against rust-yaml. The maintainer of serde-yaml I believe just took the opportunity to mark it as deprecated to ensure people don't migrate there.
I'd think of it as a smart move considering how entitled, aggressive and demanding open source software users have become over maintainers.
Not the author, but I assume he is not against you taking over. Code is still there if you are willing. (It is not too late)
I'm not a particularly big fan of YAML either I'm afraid, so I would probably choose some other way-to-learn-rust...
He'd been looking for a serde-yaml maintainer for a while, and none ever popped up.
He does this all the time to libraries. These things are correlated; the more tools you make, the less time you have to maintain any single one of them. You learn to ignore it because they're also extremely well made libraries where the unmaintained status doesn't mean anything.
AFAIK he'd been looking for a maintainer for this for a while, didn't find one, and so just marked it unmaintained. He's not doing it, nobody else wanted to, so it'd be irresponsible to leave it listed as maintained.
Sometimes you just say fuck it.
Sometimes you just say fork it.
FTFY. And vendoring a dependency is really just a hidden fork.
That raises the question: what is the current etiquette around deciding whether to pester a dependency's author to include your fixes or just fork it, especially if it looks like you will be the biggest user of that dependency? I'm dealing with that question for a dependency that I want to introduce in one of my crates, but ideally not until at least one of my own fixes is merged.
My thought process is:
1. You find a bug, open an issue. 2. If bug is a minor issue, wait. If I want a fix really bad, offer to fix it. 3. If I still want a fix really bad and author did not respond, or does not want a fix, then fork the project for my own consumption. Using alternative dependencies are also an option.
Yup, and forks don't have to be permanent.
Imo be ready to fork all the things if need be. Get comfortable with it before you're forced to.
Obviously fork it, implement your feature, battle-test it, fix and make it more generic, battle-test it again, then ask the maintainer if they would be interested in a pull request.
I mean, implementing your fixes in the first place means you are already inherently forking it.
In my opinion, you should always ask the author first. Having your changes accessible from the primary repository is much easier and better for everyone, and there is a good chance the author is happy to accept your PRs. On the other hand, they might not agree with your proposal or might want to make significant changes to the design, at which point it becomes a personal tradeoff for you.
FTFY. And vendoring a dependency is really just a hidden fork.
It's not a fork until you start diverging from upstream with local modifications.
This is exactly what we all do every time we
cargo update
without reviewing the code changes in each dep (and their deps) wrt how they impact our compiled artifact. Ultimately we're each individually responsible for what we ship, and there's no amount of bureaucracy that will absolve us of the responsibility.I think it is possible to build infrastructure that will allow entities to spread responsibility. They do it in other fields I think?
For example I can’t imagine Ford checks literally every single individual component that gets put in their cars, right? I assume they have contracts and standards with manufacturers that make the parts, probably including random spot checks, but at some point to handle complexity it is necessary to trust/offload responsibility on some suppliers.
I bet you could get those sort of guarantees from, like, Intel or IBM if people generally were willing to pay them enough (can you now? Not sure, probably depends on the specific package). But nobody would make that sort of promise for free. The transparency of the open source ecosystem is an alternative to that sort of thing, no promises but you can check everything.
Just to be explicit (since I’m sure this looks like needless pedantry), I think it is important to remember that the open source and free software ecosystems are an active rejection of that sort of liability management framework, because that’s the only way to get contributions from a community of volunteers.
There's something very different with software I think. For example, if a mechanism calls for a class 10.9 bolt with a certain diameter and thread pitch (let's say M6x1 for the sake of argument) then there are many different manufacturers who can supply equally suitable parts. They may each be dimensionally different, have different yield strengths, but they're all conforming to class 10.9 and dimensionally within tolerance. As the designer of the mechanical assembly I can trust the manufacturer of the bolts has a quality control procedure that'll make sure their bolts are good.
As a software assembly designer, I don't really have any way to say "this subcomponent remains intact" when I integrate it into my system. The compiler will chew things up and rearrange them at link time. This is a good thing! It makes the resulting binary go brr real good. But it's ultimately my responsibility to make sure the thing I ship--the compiled artifact--is correct. It's not really a valid move to blame one of my dependencies. I was supposed to review those dependencies and make sure they work in the context of the thing I shipped. Wouldn't it be a bit like the mechanical assembly designer blaming the bolt manufacturer for a failure of a bolt they modified while building the assembly (EDIT: or blaming the supplier when the assembler used the bolt in a way it wasn't designed for)? What guarantees can the bolt manufacturer reasonably give in that case?
EDIT: another thing is that source code isn't really analogous to a mechanical component. It's more like the design specification or blueprint for... something that converts electrical potential into heat and also does other stuff.
Audit and auto-update tools will be complaining about use of an unmaintained crate.
Not the case here but sometimes a library (with no dependencies) is really feature complete with no real need for changes other than security fixes.
I wish audit tools and corporate policies are smart enough to recognize this instead of nagging about "unmaintained" versions of projects with no recent commits.
I wish audit tools and corporate policies are smart
The ball is in the court of the enterprise purchaser mandating the auditing company and they don’t give a ** if your dev team has to hot swap a yaml parsing library
They should. They are paying devs a ton of money to futz around with code that works perfectly well and is just as secure today as it was yesterday.
So you have security theater and TPS reports consuming the time of some of your most highly compensated human resources.
just as secure today as it was yesterday.
Agreed but the gotcha is when a new issue comes out and there is no security fix available. It would be enough to say 'we are comfortable patching this if a CVE is found', but at that point they might as well start to maintain it.
Been doing this software thing for a bit now… while what you say is true in that it is possible in some rare cases, I think it’s a bit of a straw man exception. Code is, in the general sense, unfortunately never complete, regardless of how complete it seems in its context.
The issue is that the context is always changing. It is almost like suggesting there is an evolutionarily perfect organism. At best, one suited perfectly well to the current context can exist, but that perfect suitability falls off the second the environment changes. And the environment always changes.
I totally get the sentiment, but I think the idea that any code is ever complete is unfortunately just not a realistic one to apply outside of select and narrow contexts.
If I wrote a library to open jpegs, it correctly loads all standard jpegs, and it's free of security issues - is that not complete?
It's not like it's desirable for standard formats to one day start getting interpreted in a different way.
It's free of security issues in the current context, that's the point.
You depend on calling a standard library function that was found deficient (maybe it's impossible to make it secure) and deprecated. Now there is a new function you should call. Your software doesn't work anymore when the deprecated function is removed.
Sure, you can say your software is feature complete but you have to specify the external environment it's supposed to run on. And THAT is always changing.
You're both right but looking at different timelines.
Relevant: https://www.oreilly.com/library/view/software-engineering-at...
Thank you for describing the situation without the analogy.
It's incorrect description, though.
Unfortunately the finance analogy is sufficiently outside my wheelhouse as to be indecipherable to me. (Analogies are helpful to add intuition but they shouldn’t replace the description of the actual thing).
Maybe this description is wrong but I can’t tell.
The problem is not just the analogy but the names were changed to protect the guilty parties adding further obfuscation. Though the links are correct and you can follow the bouncing ball a little easier replacing the fake names in the article with the real names from the links:
stuff -> insta
learned-rust-this-way -> yaml-rust
to default/defaulted -> serde-yaml
As I read it: insta was depending on yaml-rust. RUSTSEC issued an advisory against yaml-rust. Some shade is thrown on yaml-rust for being unmaintained and basically a student project of someone learning the language for the first time, adding to the irony/brittleness of its unmaintained (though still working) state and now the RUSTSEC callout. In insta trying to switch to some alternative to yaml-rust the next most common library serde-yaml preemptively announced its unmaintained status. So insta just vendored yaml-rust and called it a day, which currently works to remove the RUSTSEC complaints but doesn't fix the long term maintenance problem.
This isn't accurate. OP's article says they depend on yaml-rust (https://github.com/chyh1990/yaml-rust), which is now unmaintained. Simultaneously, serge-yaml was marked as unmaintained to prevent people from migrating there en masse.
Sorry, indeed it's been about the other library. However, these issues are connected: dtolnay is a co-owner of yaml-rust too, so either way without him YAML in Rust has a https://xkcd.com/2347 problem.
so either way without him YAML in Rust has a https://xkcd.com/2347
You seem to be misunderstanding the comic you link.
With him that was the situation.
On crates.io, serde_yaml has 56m lifetime downloads and 8.5m recent downloads, and from his own announcement David hasn't used yaml in any of his project in a while, yet has kept plugging at it until now.
And yaml-rust has 53.5m lifetime downloads and 5m recent downloads, and has been unmaintained for 4 years.
Well, whether this is a "win" depends too much on the definition of "win" for me to care to debate. But there are certainly advantages; a vulnerable code path that is never executed and can not be reached from the external library is now a safe code path. A nerve wracking one to be sure, but safe.
Another advantage of vendoring like that is that you do have some tools for attacking the code now. If you have solid test coverage on your own library, you can now run your library and run code coverage tools on the newly-vendored library. Fixing the library may be a challenge, but you might be able to just go hacking into it and deleting anything your code doesn't touch relatively easily. Depends on the structure of the vendored library, of course. If you end up deleting all the vulnerable code, you do have a clear win. If you discover you were hitting some of it after all you may end up with an even more clear win.
Or, to put it another way, forking a library in general for public maintenance is a big commitment. Vendoring it and hacking it to just the bits you need is much smaller. Even if you don't actually perform this pruning, just the act of making that pruning easier is itself progress.
There are disadvantages too, certainly, but it's not all bad.
Worth pointing out that all third party deps at Google have to be vendored. By policy. And generally only one version of said dep is permitted for the entire massive monorepo. (This may have changed since I was there.) And each third_party dep has a set of designated responsible individuals / OWNERS.
This has the effect of keeping things relatively sane.
But this enforced discipline can work well at an org like Google that has plenty of time and $$ and isn't dedicated to a "move fast and break things" philosophy. Not sure how well it would work for a startup.
You don't discuss why they do it or the downsides. "Google does this" is not good enough of an argument.
For the why, could be that the build would be dog slow if a network requests was made for every one of a bajillion dependencies. Or to avoid breaking the build if something is janked externally. Could be because C++ has no modules/libraries. Could be for the lawyer's sake.
Is any of that applicable? There are also other ways of vendoring Rust crates, e.g. mirroring them to an internal registry. That can have fewer downsides.
Security is one big reason. Avoiding link time or runtime conflicts is another. And, yes, having outgoing network calls for builds is a problem. Not because of performance concerns, but because it's a security and reliability nightmare. But consistency across the whole codebase is drastically important.
There are many many reasons but overall sanity and security concerns are paramount.
But also, read the original article referred to here... and the pain points seen there. Be careful about what you depend on.
Security is one big reason. Avoiding link time or runtime conflicts is another.
I mean, most Linux distributions do the same thing - anything that's "vendored" as part of some project is supposed to be patched out from the source and pointed to the single system-wide version of that dependency. And Debian is packaging lots of Rust crates and Golang modules as build-time dependencies within their archive.
Rust has the `cargo vendor` command to vendor all crates.io and git dependencies for a project.
Looks a lot like a Linux distribution, for example, Fedora, which has the same policy to reduce maintenance burden.
The policy in Fedora is precisely that upstream must not vendor (bundle) their dependencies https://docs.fedoraproject.org/en-US/packaging-guidelines/#b... https://docs.fedoraproject.org/en-US/packaging-guidelines/Ru...
This seems like a unique solution for a Google-sized programming enterprise, right? Google is big enough and has enough programmers that they can have their own internal ecosystem that looks a lot like the whole open source universe (other than not being open to the outside world).
I mean, they're all open source dependencies. Just moderated.
I’m completely guessing here but this looks like one relatively technical reason Google keeps killing products.
That guess is not accurate. There are plenty of other reasons though.
This is actually a great outcome. All dependencies are a security risk - the argument for not vendoring every dependency is that you should keep external dependencies up to date to avoid known security issues. If the external dependency has nobody watching it any more then that argument disappears, and that external dependency becomes a liability.
The fact the dependency was marked as abandoned and started getting flagged in security reports is what you want. That lets you make an informed decision about whether to vendor it (which removes the "bad actor sneaks something in" risk) or do something else. It's great that the Rust build system allows that so easily.
Author here: the result is now that the debt got absorbed into a project that uses the fork exclusively. It’s less likely that folks will look for security issues. It would in my mind have been better for folks to use an abandoned shared library where at the very least if a security incident popped up we would have all been notified and dealt with it at once.
The debt has been absorbed and I don't deny that, but it has at least been absorbed in a way that makes paying it locally easier.
Given that the description of the problem includes generally not being affected by its issue I read that to be the rather common case in which one is just using a relatively small fraction of the library. To be concrete, let's say it's an image library, and it turns out there's a PNG parsing vulnerability, but your code only ever uses it for GIFs. If you vendor it you also have the opportunity to go in and just yank PNG, JPEG, TIFF, etc. support, and depending on the situation, plugin support (maybe you can trivially tweak your code to go directly to the JPEG support without passing through anything else).
I'm not saying this is a perfect solution with no downsides, but it's not all downside either.
Of course, you need the aforementioned solid test coverage. Without that you're just flying blind. That's another thing about the debt metaphor... it can compound, but unlike most monetary debt, code debt can compound on you unexpectedly.
Vendoring a pinned dependency just looks like dependency theater. You can let it sit there as a pinned dependency and it will do just as much harm as if you vendor it.
I get the sense (only a sense because the author is a bit coy, but sibling comment by him seems to confirm) that this was more to appease these tools that look for “bad dependencies”.
Even if it's maintained, you don't know how good it is security wise.
I wonder if there are tools that library maintainers can use to calculate transitive code coverage of their libraries, eg which other lib test suites use them?
Could be an interesting test strategy, in some sense it’s directed fuzz testing. (With other libraries providing the random-ish invocations.)
In case I'm not the only one that didn't immediately understand CDO the unexplained acronym - my guess is "collateralized debt obligation" based on multiple uses of collateralized in the post.
https://en.wikipedia.org/wiki/Collateralized_debt_obligation
My first guess was "chief data officer".
You gotta watch The Big Short. That's where I learned what a CDO was
Great movie- but didn't remember this acronym.
I think they make a joke out of the explanation. Iirc, Margot Robbie explains it, while taking a bath.
I just realized there's a whole generation of people young enough that they weren't reading headlines back when CDOs destroyed the world's economy. I feel old.
I came of age during the S and L crisis. Been alternating between increasing prosperity, magical technology, idiotic wars kept going by the side that wants might makes right against the side of people that want an ordinary and beautiful life, and scam based economic collapses, against a backdrop steady worsening of global warming and global biodiversity ever since. Most fascinating life, I hope my kids enjoy their time half as much as I have. Not without problems, but the people, the learning, the flowers, the maths and the holding hands, hard to imagine better, if we’d just get out of our own way.
Embarrassingly, was certainly old enough to be reading those same headlines / being familiar with housing crisis, guess I'm just not an acronym person.
Correct, with the implication that by creating incentives to bundle junk tech debt into 'AAA' packages (packages with good reputation), the Rust ecosystem is heading for a bubble (and eventually a collapse) because the rating agency that's supposed to regulate things has been gamed.
Do you think that implication is correct? If so, how do we fix it? Of course, companies should pay for what they use, but how do we practically implement that?
Great question. One thing to remember is that the value of a piece of software that’s heading towards being un-maintained is much lower than one that has a clear maintenance path. One should be wary of single-maintainer projects. That said, the number of maintainers and the amount of code per maintainer would seem to be the important variables. In this case the code has moved from a repo with one (albeit absent) maintainer to another with one maintainer. Let’s call that neutral. However our new maintainer now has much more code to maintain so the risk has measurably gone up. It might be worth also taking into account how much code in total someone is responsible for across the cargo ecosystem.
I was thinking of https://en.wikipedia.org/wiki/Collaboration_Data_Objects. ;)
The analogy here is that the CDO is a financial product that's built from other debts.
Specifically, it was a piece of partial ownership on a bunch of mortgages in 2008. The issue here is that when the junk mortgages defaulted, the CDO also went sideways [1]
In any case, what the author is thinking about is more "systemic risk" (think leftpad or cascading DNS failures) rather than debt-based analogies. One big reason 2008 became such a big mess was the systemic issues, not building products on debt [2]
1. Fun fact - before 2008, they had started making CDOs out of CDOs because of of the insatiable appetite for mortgage-backed financial products. When mortgages are underwritten for people withouth a job, and packaged in a product with a AAA credit rating, it turns out it's a profitable thing to sell.
2. Also the fact that Lehman and Bear Sterns had leverage ratios in the 30x-40x range, which is insane.
Slightly tangential, controversial thought: any source based package manager that doesn't enforce a legal right for the registry to forcefully take over maintainance of published packages from their creators is doomed to have awful problems: neglect, malicious change, malicious removal, impostors...
If a package is deemed important enough for the larger community, there needs to be a way to rip the package registry entry out of the original owner's hands and to make it point to a fork. Natually, such a move will often involve a lot of drama, but it can actively protect downstream users.
any source based package manager that doesn't enforce a legal right for the registry to forcefully take over maintainance of published packages from their creators ...
This seems to be a non-issue. The beauty of open source is that you can fork it. The actual issue is that it requires time and effort to maintain an open source project and it's not easy to find someone else who has some time to spare.
Yeah, finding free labor isn't light work. It's not trivial to externalize costs to unfortunate people. But the entities that succeed in this subjugation are the ones that are rewarded with some substantial wealth and power.
No, there are issues in practice. I have seen many published libraries getting neglected by their creators. Unless the community is actively channeled in this case, the result is a set of lackluster forks by people who only make some random changes. As a potential new user, you don't know what to pick. In the worst case, you need to create another fork, because none of the existing ones fixed the issues you encounter. If you take that public, the mess only gets bigger.
This chaos can be reduced with a single "blessed" fork that actively accepts reasonable maintainance patches. There are fewer incentives for competing forks and dependencies (direct and transitive) of the original package can automatically benefit, too, because releases are made under the original package name. This is good because the neglect problem can be several levels deep.
There is the issue of manpower. There are two arguments here: one is that the bar for package takeover needs to be set such that community interest in a fork is high enough and the fork needs to be managed by the package registry owner for several reasons. One is that maintainers need to be appointed through the registry provider to keep the nodel functional. Maintainer appointments can be passed on after extended inactivity. The other argument is that a package registry provider puts themselves into a trusted position by default and has to actively maintain that trust. Thus, the burden of managing forks and fork maintainers shouldn't necessarily be seen as inappropriate.
A source based package manager which steals ownership of its packages when it sees fit is one that should struggle to attract contributions. It appeals even less than the copyright model where all contributions are automatically owned by some specific group.
Though an argument could be made that github is a host of free software dedicated to the copyright infringement of said software, at least it doesn't currently try to usurp the nominal ownership of the packages.
think of it as an NMU (non-maintainer upload)
https://wiki.debian.org/NonMaintainerUpload
also, names in a registry should be "leased" not forever eternal perpetual immutable assigned to some guy
Two counter arguments:
The package registry entry isn't the package. At no time is the original creator deprived of their rights to their package. Any fork will have to respect whatever license the original creator assigned. Open source licenses are allow forking by design for a reason.
Also, this whole thing needs to rest on a transparent process that doesn't make takeovers easy. There need to be clear criteria when takeovers can even be initiated, like extended inactivity (think 6 months or more) or unresponsive maintainers when serious issues are encountered. Also, there is not much harm in a mechanism for reclamation by the original package owner.
The alternative is the Kik NPM problem. It's bad both ways.
The creators of Cargo didn’t want to have namespaces. That seems like the biggest mistake. Then you never get official-looking names just because they were the first ones to ask for them.
If a package is deemed important enough for the larger community,
It may be done at package level at best but not at registry level. Since we are talking community it is not a customer/vendor relationship, most packages are just important enough to be available for free. It may also be at some humiliating amount like "here is 10 dollar/month now maintain 100K packages for us.
Not sure how this could actually work, what with trademark law and stuff. It would be extremely legally interesting.
what is CDO?
Collateralized Debt Obligation: https://en.wikipedia.org/wiki/Collateralized_debt_obligation
It's a financial product that's built from other debts.
Specifically, it was a piece of partial ownership on a bunch of mortgages in 2008. The issue here is that when the junk mortgages defaulted, the CDO also went sideways.
Fun fact - before 2008, they had started making CDOs out of CDOs because of of the insatiable appetite for mortgage-backed financial products. When mortgages are underwritten for people withouth a job, and packaged in a product with a AAA credit rating, it turns out it's a profitable thing to sell
Collateralised Debt Obligation
It seems like the RustSec "unmaintained" notice worked as intended. Unmaintained dependencies represent a latent security risk as they will almost certainly not respond to any future vulnerability; identifying this risk before an incident happens is much better than trying to figure out response in the moment during an incident.
Armin's answer here, of taking on explicit ownership and responsibility for the third-party code they use, seems like a reasonable and socially desirable response. Yeah, I get that it's annoying, but this is (IMO) pretty much how open source is supposed to work.
A latent risk for sure, but this is being handled like an urgent risk because everybody's CI is lighting up. But it's not an urgent risk. So I do not think the system is working as it is intended, although it's hard for me to know exactly what the full intent here. (Since the resulting behavior is likely a confluence of several different systems, so I'm not sure there is one single coherent "intent" per se.)
Yeah, I'd put the blame for that on the tools / systems which consume and report this kind of vulnerability data. They (at least as far as I've seen) don't really differentiate between severities of vulnerabilities. On the one hand, a strong preference toward "just patch" for vulnerabilities can make a lot of sense (the more that mechanism is flexed, the better it moves when you really need it, and rapid integration of fixes reduces time for exploitation by attackers), but here it's applied to a maintenance notice that ought to be treated a little differently.
Nonetheless, I think RustSec's feed is working as intended, and I'd put the expectation on the consuming tools (including `cargo-audit`, which I believe is largely maintained by the same people that maintain the RustSec vulnerability database) to change.
Armin's answer here, of taking on explicit ownership and responsibility for the third-party code they use, seems like a reasonable and socially desirable response.
It seems like he vendored it in order to avoid the useless alarm bells going off. Don’t we have a `Cargo.lock`? Just keeping the status quo seems fine.
But other people didn’t like it because of these automated alarm bells, hence it becomes “socially desirable” to do something that doesn’t materially make a difference.
This brings up a great point, that tech debt is literally that; debt. Debt is neither good or bad, it's all down to how you use it. Properly utilized, debt makes things possible that wouldn't be otherwise. And it can be maintained indefinitely, so long as your payments and interest are reasonable.
I agree with "Debt is neither good or bad, it's all down to how you use it.", but this not the central point of the article.
The article's differential contribution relative to other bog-standard tech debt articles is thinking about it as derivative. In the financial world, the behavior of derivatives is more complex and less predictable. Here is an excerpt showing the tech-debt-as-derivative metaphor in action:
... someone called you out of that debt. If we really want to stress the financial terms this is your margin call. Your users demand action to deal with your debt.
Remember: tech debt is not "literally" debt. It is a metaphor; a useful metaphor when used mindfully.
Great point. Like in real life if the cost of taking that debt is less than what you gain from it then usually it is the right choice to take it. Like going into a small amount of tech debt to ship a critical feature where some people will refactor and rewrite the whole thing not delivering value over long periods which stifles growth. Like it or not but debt is the basis of growth.
Of course the issue is like with real debt also - how do you estimate the costs/gains from taking it and you're under risk of going bankrupt if not managed properly.
I'm excited for the Michael Lewis version of the Rust library ecosystem.
I'm guessing you write this because Lewis "is known for his nonfiction work, particularly his coverage of financial crises and behavioral finance." (Wikipedia) How do you think Lewis would frame up the conversation? Where would he take it? I see much low-hanging fruit; the article linked above isn't exactly lengthy nor rigorous. Detailed analysis of supply chain security probably should include the temporal dynamics; e.g. factoring in automated tooling as well as herd mentality.
it is rumoured Flash Crabs will be out in the fall
This whole situation seems a little ridiculous. If code works and has for years, who cares if it is unmaintained? As long as you don't need to modify it and know its limitations and capabilities, it's fine.
Code doesn't go bad on its own. I've borrowed or integrated code from decades ago just fine many times.
Personally I'd just ignore any and all complaints about that library and move on.
I've borrowed or integrated code from decades ago
Isn't that what the author did by vendoring the library within his own project? It amounts to taking over maintenance for the bits of code you use. Sometimes this is bad (copy-and-paste coding, possibly duplicated effort) but when the third-party component is totally unmaintained it becomes quite excusable.
Just a bigger copy-paste from Stackoverflow than normally.
Have seen the same pattern in the JS npm ecosystem.
Npm audit predominantly cries wolf on security issues and rolling things in, license permitting, is one of the most reliable ways of not getting inundated with bogus issues from users (who either don’t understand the context or who don’t care because their employers’ policies around these things was created in a space divorced from reality.)
(No, that regex being used as a part of your build pipeline’s transitive dependencies’ codebase is not actually something which can be exploited for a DoS attack.)
The ”issues” for deeply transitive dependency can be especially annoying to work around. Trouble is that the way things are structured you often can’t technically establish ”no, we will never enter that codepath so aren’t affected by flaws in it” or ”the only way we enter that codepath is with trusted input in an offline setting”, etc.
Trouble is that the way things are structured you often can’t technically establish ”no, we will never enter that codepath so aren’t affected by flaws in it”
And even then, you could enter the problematic paths in future releases as your dependencies update.
No, that regex being used as a part of your build pipeline’s transitive dependencies’ codebase is not actually something which can be exploited for a DoS attack.
Yeah, issues in dev dependencies are such a headache in the JS world. There exist scenarios in which these matter (a compromised build tool injecting malicious code into the lib you’re building) but they are vanishingly rare and hidden under the tidal wave of DOSable regexes that don’t matter at all when you just call them in your build. Couple that with typical build tools having a transitive tree of 50 gabillion dependencies and it’s a real slog. I think the tooling for reporting on these issues needs to differentiate “exploitable if you redistribute” vs “exploitable if you use in a build pipeline”.
Regarding the state of the YAML crate, if you can live with StrictYAML there's always https://crates.io/crates/strict-yaml-rust
Woah, nice! This is what I always wanted yaml to be.
Somewhat fortuitous that someone already forked yaml-rust and rewrote it in pure Rust to create yaml-rust2 (https://github.com/Ethiraric/yaml-rust2/blob/master/document...). Pretty cool that the fork fully passes the YAML test suite as well and is more performant on benchmarks. The migration appears straightforward too.
Ultimately, the problem remains - we're dependent on the work of others who are happy to provide us with free labour for now, but may not be in perpetuity. I don't know that there's a way around that other than compensating them for their time and effort and hope they continue their good work.
Somewhat fortuitous that someone already forked yaml-rust and rewrote it in pure Rust to create yaml-rust2
yaml-rust was a pure-rust implementation, that's literally in the tagline:
A pure rust YAML implementation.
serde_yaml was arguably less pure-rust, as it relied on unsafe-libyaml, which is a c2rust translation of libyaml.
> Now that junk tech debt is suddenly rated “AAA”.
I think by "suddenly", the author is saying it doesn't make sense for the same code to have a better debt rating when vendored than it did before. But that's only looking at the value of the code itself, which misses the most important part of the total value proposition.
When a maintainer vendors code in, the maintainer owns that code now. If you're an active maintainer vendoring in code from a dead project, you are increasing the value of that code because now there is an active human who may respond to issues, review pull requests, and fix bugs.
By another analogy, giving a neglected pet to a new owner increases the value of the pet because it will be better taken care of, healthier, live longer, etc.
Only for those who use the dependency indirectly. This will likely see very few eyes to find bugs. The maintainer will also have a ramp up in having to deal with a large, unfamiliar code base that will be a hindrance to implementing fixes or reviewing them.
ITT: People who have not read or seen The Big Short
The 2008 mortgage crisis is now 16 years old, and the stock market had recovered by 2011. So presumably anyone who entered the work force afterwards might have little sense of it, which would be anyone under the age of 31 (or 35 if you count college).
you just merge that code into your own library
License-permitting, right? right?
The Rust ecosystem is nearly uniformly Apache2/MIT, so this is more likely than not.
Guess I'm one of the annoying users who complained when armin's Lektor (https://github.com/lektor/lektor) started going dormant back when, but I loved it for a while. I'm on Astro now, but a big thanks for helping a younger version for me.
PSA: you basically have to have seen the movie "The Big Short" to understand the analogy in the article. Or at least this scene: https://www.youtube.com/watch?v=xbiDrzTd8fE
It would be good if there were some rating agencies that rated the big dependencies and if the government would bale out the packages that became too big to fail. However this means there is need for money. Until companies collectively understand that having a stable base that reaches further than the core language and pay taxes and set up rating agencies all languages will just be a mess of unmaintained dependencies with security issues.
If it’s a direct dependency, vendoring and then deleting everything you don’t use seems pretty reasonable? This is “a little copying is better than a little dependency.”
Make enough changes and it’s not vendoring anymore; you made the code your own.
If we're going to use financial analogies, we should also address the elephant in the room: actual money, that is, paying maintainers. I've just filled out the contact form at Tidelift, even though it may practically be too expensive for my tiny bootstrapped SaaS company. I'm open to other ideas.
Yup, you vendor the dependency. I've been doing this for 20 years with basically any dependency that's close to "done" and has slowed down development and maintenance.
Having said that, I've not worked in languages where "batteries are not included"
One disadvantage of merging it into a more successful but also (most probably) bigger project is that it will take even mooooooore time to compile :D
Lol. But really often it’s better to do that and absorb the code in. Not sure what insta is and whether it makes sense in this case.
So GitHub is apparently so bad at presenting repositories and forks that there are busy beavers running around asking “is this maintained” and reporting to some registry or something if they apparently aren’t.
- Anyone know this guy?
- Has he logged on in the last year?
- Can someone contact his aunt in Xinjiang? Maybe she knows something
I experienced the same thing with a repository that wasn’t active. I mean a repository might be feature complete so why should it be active? Well other people wanted more from the repository. But not the author apparently. That’s fine. Well turns out that there was an active fork which was vastly more “featureful” by design. And a PR to that repository took less than three hours to be merged. How did I find that fork? Honestly I think it was mostly by coincidence since I saw the fork owner posting on some issue in the original repository.
Spacemacs was experiencing a reviewer bottleneck for a while (I don’t know what the status is now) in part because the repository owner was not very active. That got partly solved by more people getting commit rights apparently. But why don’t forks pick up the slack instead of waiting around for commit rights? Hijack the PRs and review them in your own forks. Merge them if they are good. Then come back to the upstream and ask the main guy to merge them. If he trusts you and would have given you commit rights anyway then it shouldn’t be a problem.
GitHub should prominently tell you about other forks if they are notable in some way:
- More activity (most important)
- Maybe more stars
Because then you don’t have to hope to God that the repository owner logs on every two weeks and keeps his thirty repositories updated about their maintenance status. (Honestly who doesn’t have dusty old code that works for them which you haven’t even thought about for months or years?)
Why is this so difficult? All repositories are peers after all.
Is there some way to build a
cargo vendor --aggressive
which might prune all the code from your deps that is "dead" to your crate? Would it make the "review your deps" problem more tractable? This is a tangent to the article, but related in that ultimately we're responsible for our choice of dependencies and all that entails. It seems like there might be room for tooling to help us better take responsibility for what actually gets compiled into our crates.Ok, but, it's a fork of the code. Not a trade. Can you fork debt? If so, I'm a quadrillionaire.
I like the analogy but when you merge the junk into your repo, it doesn't change in rating but is rather paid in full by you, as you assume it on your own tech balance sheet.
Yeah. The transitive dependency graph for small projects can hit 100 pretty quickly, which instinctively worries me. Unlike node, however, library quality is pretty good in quality and how they respect SemVer, and static type checking helps as a first line check.
The library quality is mostly fine -- however crates.io is basically unmoderated and it will get worse and worse overtime as things bitrot and collect dust or the namespace gets abused (already happening).
The biggest problem is the exploding transitive dependency graph, including multiple versions of the same basic utilities.
Even small projects can suffer from this. It's too easy to reach into the grab-bag at crates.io and pull a little utility out and use it. I've personally built very small things only to find multiple versions or implementations of e.g. sha256 or rand or whatever in my own dep graph, and it gets really hard to prune them out.
At work we're fairly diligent about this but still it's exploding. And when you consider the matrix of minimum-supported toolchain versions for different projects, and if you're stuck on an older Rust toolchain, it's even more pain.
Rust projects starting from scratch could consider vendoring their dependencies rather than pulling from crates.io. But support for vendored dependency in Cargo is weak/bad. You run into all sorts of nightmares especially when using workspaces.
It's heresy and I'll get downvoted for it, but: More and more I lean towards using bazel or gn/ninja for fresh Rust projects, and walk away from cargo & crates entirely.
Cargo (+ Crates.io) was delicious to me at first, but it's a meal that tastes worse the more I have of it.
EDIT: it strikes me that I didn't say why exploding dependencies bugs me so much. There are logistical reasons for sure (competing APIs, compile fails, binary bloat, etc.) but the primary reason is: exploding complexity. Every additional dependency is now a potential problem for future me/future team.
This is the first step of your journey.
Next step is realizing the same about the language itself.
:-) I've been around in the industry long enough to know there is no perfect tool. Rust itself has reasonably decent foundations, and fills a niche that was missing for me for a long time. There are no mainstream-acceptable alternatives to C++ for (GC-less) systems level programming that bring in a reasonably modern (ML-ish) type system, pattern matching, proper modules, etc.
However, I also think the vast majority of Rust projects out there right now are using the wrong tool. They're using Rust to write web services and web sites, and the developers are coming from places like NodeJS, TypeScript, etc. It'd be fine, but it's I believe pushing the emphasis in the Rust language community in that direction.
I worked for years in the relatively sane subset of C++ that we used at Google. It's fine, I have to do C++ here and there for work, and I could even go back to it if I had to. But I mostly like Rust, and would miss it. Zig made some decent choices, but others I don't necessarily agree with, but I'd consider it for embedded or low-level dev of various types
Of course they are, same as what had happened with (to) C++. And the people that use Rust (or C++) because it's "fast" are mostly the same that write code which would be faster if it would have been written in Go, Java (Kotlin?), or C#. I guess every generation of programmers must make the same errors.
Yeah, I've been around this block many times. I've been in the industry since the mid-90s. The problem comes down to that people have all sorts of different motivations in why and how they use technology, and organizations and projects are difficult ships to steer, and the palette of available choices is not always what we want.
I prefer not to consider other people as ignorant or stupid. They just have different motivations or reasons.
But back to the topic: I do think Cargo should have started from looking at Maven as an engineering foundation rather than NPM. Maven shipped from the start with solutions to many of the problems that Cargo is sick with because they were not considered.
Oh, I didn't want to imply people are stupid, after all, I (and many other people) made the same error 25 years ago.
But talking about Cargo (the last time I used Java there had been no Maven yet), the packages really need at least some sort of namespaces. But I don't think that (newer) languages without a really "battery" standard library can evade the problems of "too many transitive dependencies".
Yeah I agree in principle. There's lots of people who defend it but I think it is a mistake for the Rust stdlib to be missing a lot of quite basic common stuff (random numbers, sha256, md5 are obvious ones to me for example.) They could even break said lib up (stdlib, stdlib-ext, stdlib-embedded) if necessary. But status quo is just inviting future disaster in my opinion.
And don't get me started on the async story. Yikes. Rust is the language that Tokio ate.
In the spirit of being charitable, I'd like to hear more about your journey here.
At the same time, to some people the comment above might come across as snide and/or unjustified. I can't get a confident read on the author's intended tone.
Could you point us to some relevant discussions (GitHub, etc) so some of us can weigh in and possibly contribute to this pain point?
No, sorry, because these are things that have happened at work, in my paid job, not my hobby, and I have higher priority things to do than help Cargo clean up its mess right now. I'd have to go dig back through chat logs and tickets.
But basically: vendored (git submodule in this case) project that was workspaced, added as a dep inside a workspace... Blew up badly with general workspace complaints. And doesn't help that at work we have to accommodate working with older toolchain versions because we ship a hardware product with that requirement.
Makes statement as a minor subpoint to my main point about crates.io and dep explosion.
But sure, flame away, you're adding a lot to the conversation /s
Thanks; I get it. I was thinking maybe the open source community has discussed this in some detail. I'm not actively involved in any such discussions, but I did a quick look and found that "Documentation for cargo vendor use cases" [1] is a decent starting point to jump in. In that thread a user named 'rimutaka' has a comment titled "cargo vendor-related questions asked by the community" [2] of related conversations.
[1]: https://github.com/rust-lang/cargo/issues/7520
[2]: https://github.com/rust-lang/cargo/issues/7520#issuecomment-...
One case that can cause you trouble is that if you have a workspace that contains two different programs with deps that specify incompatible versions of another dep, you can't build either :)
Could you be clearer about this? What's wrong with cargo vendor?
I comment on this below, but just try doing a path = "" dep to a 3rd party workspaced project inside a workspaced project and see what happens. I'd have to dig through old chat logs to find the specific explosions, but cargo did not like this.
(It's possible it's been fixed in recent Cargo versions, but that's not available to me.)
But in the end, cargo is a tool primarily designed for building crates that depend on crates in crates.io. Even workspaces themselves are a bit of a half-baked afterthought.
But cargo really should have started by looking at Maven for inspiration -- a) a moderated repository and b) Maven has much more expressive support for version restrictions etc.
Instead crates.io feels like it's going the way of the world npm.
Anyways, sorry if this sounds like "get off my lawn", I'm grouchy today.
AIUI, this is not how you use vendored dependencies in cargo. You're supposed to edit the .cargo/config.toml for your current project with the output 'cargo vendor' provides.
I believe I did in fact do that as well and ran into the same issues. But I could revisit.
I would suggest Buck2 (which is written in Rust, so you can use the existing tooling to build it) https://buck2.build/ https://github.com/facebook/buck2
Rust support is also rather good ;) https://github.com/facebookincubator/reindeer But you need "fixups" to get many cargo packages to work with Buck 2: https://github.com/facebook/buck2/tree/main/shim/third-party...
Interesting, I'd heard of buck and classified it as a blaze/bazel clone made by Xooglers who ended up at Facebook. But didn't realize buck2 was written in Rust.
One of the specific weaknesses I found with Bazel for embedded development was support for multiple toolchains was really not great. I'm sure it's improved, but I found the gn/ninja combination much better.
It did improve.
However, I'm procrastinating right now by reading HN instead of fixing our multi-compiler requirement toolchain setup for Bazel, and I am contemplating resigning from my job and IT altogether. I hate Bazel at this point.
I'm with you.
If you find a good replacement for SW eng as a career, let me know.
Two dozen dependencies or one and a half?
Depends on when each starts depending on a different version
I though rust was supposed to have a vast standard library?
It has a large standard library compared to something like C or C++, but it’s small compared to a lot of more recent languages. Intentionally so, since they wanted to be able to create smaller binaries.
Small binaries has nothing to do with the reason for that. Build tools will only include what's needed either way, so e.g. your Hello World program will have parts of std::io and std::fmt, and not really anything beyond that. So you "pay for what you use", regardless of whether it's part of the standard library or an external library.
I think in Rust the choice is more one that originally prioritized viability of long-term maintenance of the project by keeping the volume and surface of the standard library small. For all more recent inclusion the inclusion criteria seem to be either "small ecosystem functionality that everybody is using and has proven to be valuable" (e.g. once_cell) or "what is close-to-optimal solution for critical parts of the ecosystem that we have to include to minimize ecosystem splintering" (e.g. the work around std::future). I wouldn't be surprised if there are actual written down criteria for std inclusion somewhere.
I’m not aware of the current state, but in the old days, it was roughly:
1. Is this useful in virtually every Rust program?
2. Is this something (data structures, mostly) that requires a lot of unsafe to implement?
3. Is this a vocabulary type?
Any of these is a slam dunk, anything else is more subjective.
That's too strict a criterion for a standard library!
Sure, different people can have different opinions. But it’s also not literally true, in the sense that it’s only one of three criteria, but also because it’s more of a conceptual framing than something to be taken exactly. There’s just a high bar of usefulness required.
You can tell that this isn’t literally true just by looking at the contents of the standard library.
That doesn’t make sense, you don’t link against the entire rust stdlib, you compile directly against its source. You don’t get more than what you call.
Actually, this is not correct at present. Compiling the stdlib as part of building a project is only supported as a special option in nightly rust.
You don’t compile the Rust standard library from source, you link against a pre-built one. There is an unstable option to compile the standard library from source.
That said, you’re right that “to keep binary size down” isn’t why the standard library is small. Heck, that you don’t compile from source means sometimes binaries are larger than they have to be, even with LTO! It is largely because anything that’s there has to be supported exactly as is forever, interface wise, and for many things, we did not have the confidence on what the right interface would be. Furthermore, it’s harder to maintain the standard library than it is a package out of tree for a variety of reasons.
For the opposing view, "the standard library is where code goes to die".
I disagree.
It's more like the code has ascended (https://nethackwiki.com/wiki/Ascension). It has won because it is useful enough and mature enough to accompany the language and serve everywhere.
Depending on what it does, standard library code may be used everywhere. For example, would you say that the Rust's Vec (https://doc.rust-lang.org/std/vec/struct.Vec.html) is "dead"?
It can still get new features added. It will still get bug fixes. But its primary APIs are engraved in stone, never to change because it would break all the code that depends on it.
Granted, some code that goes into standard libraries will eventually fall out of use, or will be replaced by something better. In such cases, the code should be marked deprecated and eventually removed in a future edition of the language. In that case, it is like the code retires or could even be forked into a community-supported library in case it does still need to be used.
The eventual removal part doesn't tend to happen. Changing the API to a better one breaks people so that's out too.
Adding more stuff to the interface is ok in terms of things continuing to work and over time makes the library rather large. If the new interface is better there's a good chance it's different and thus inconsistent with the previous code.
Bug fixes are tricky too. If they're semantically observable people probably depend on the current behaviour.
Languages vary in how well they handle this. Python and C++ are the usual mistakes are forever example. Perhaps rust or c# have a better strategy.
Training users to act on deprecation warnings and ensuring they actually have an alternative would be a good play. Lua manages to break things a bit over time. Python tried it once and will forever fear the flame. C++ refuses to recompile anything and fears the word ABI.
Hyrum's Law (https://www.hyrumslaw.com/) definitely does come into play, but bugs do need to be fixed. Good communication is essential so that library users can adapt. In extreme cases, jarring fixes can turn into new APIs.
Rust's Editions (https://doc.rust-lang.org/edition-guide/editions/) offer a nice way to gently break backward compatibility in order to keep everything progressing.
It's happening in Python 3.13: https://peps.python.org/pep-0594/
.NET really is something special. I guess that’s what billions of dollars buys you? But even still the saying is true about many other standard libraries.
If only you knew comp level for DevDiv in EU or occasional layoffs ;(
There is no magic trick, just people (both hired and community contributors) who invest their experience, skill and ambition to make great technology.
Same for Go standard library. It is excellent and curated by "billions of dollars".
The Rust team prioritized keeping the standard library small, and allowing important but inessential functionality to live outside the stdlib and keep their own release schedule.
MS has massive resources and can align the many many modules of .NET into major releases. The Rust project does not.
There are crate "recommendation lists" like this: https://blessed.rs/crates
While these help, Rust's small standard library has slowed Rust adoption where I work because our IT security team must approve all third-party packages before we can use them. (Who blindly downloads code from the Internet and incorporates it in their software?)
This means that unless there is a compelling need to use Rust, most teams will use C# because .NET is approved and has almost everything. I prefer Rust to C#, but we are not using it that widely yet.
It's not a fair comparison, but it is how things currently are.
Do they also review the original tooling? Why would one single out third-party packages?
Everything used for software development is explicitly approved. This includes programming languages, compilers, debuggers, IDEs, etc. We are primarily a Microsoft shop, so the majority of our development tools follow that direction.
For FLOSS libraries, the approval process covers both IT security review, a source code scan / static analysis, as well as a legal review of the package's license to ensure it is not on the prohibited list.
This makes management happy since it prevents potential security and legal issues. It keeps our customers happy since they get quality software made from fully traceable components.
blessed.rs is fairly new. And if you look for yaml on this page, it links to serde; and serde_yaml being deprecated (with no blessed fork yet) is precisely part of the issue discussed in the parent article: https://lib.rs/crates/serde_yaml ; and that's perfectly fine for the maintainers to declare, it was all open source with no warranty whatsoever.
I wonder if there is a scope for a "opt" / "extras" type level where libraries for stuff super commonly used, like JSON/YAML/ssl etc can be placed. It doesn't have to cover everything the defacto third party libraries do (that will be too much and not practical), just including happy paths should be enough for most people to just include the meta package and get the most generally used features instead of including a ton of third-party libraries.
My knowledge about Rust's library system isn't great so there are probably several gaps in my argument.
Several people have tried to create such a thing over the years, but the ecosystem never uses them. People objectively prefer to pick their own set of packages.
By BCL you mean "Base Class Library"? From where I sit (having worked with many languages in many sectors), BCL isn't an acronym in common circulation.
No, but it's an acronym in common circulation in .NET land.
You're spoiled by .NET, however if you look beyond BCL, we now have:
- WinForms, WPF, UWP, WinUI 3.0/WinAppSDK, MAUI
- WebForms, MVC Framework style, MVC Core style, Razor Pages, Blazor, Hybrid Blazor (tryig to fit everywhere there is a Webwidget from the previous line)
- Latest design decisions being driven by Azure and ASP.NET requirements
What kind of decisions driven by Azure do you have in mind? What kind of impact does ASP.NET Core (or EF Core for that matter) have on CoreCLR and NativeAOT?
This is a feature, not a bug - although the situation could probably still be improved.
"multiple incompatible ways to do async" is another way of saying "you can use async on a tiny microcontroller without any OS at all, or in userspace on a 128-core Epyc CPU, or in the Linux kernel"
That's a unique feature of Rust compared to any of the languages with a single executor baked into the language like C# or Javascript. It does however come with costs, and one of those costs is that people who just want to write a web app are exposed to details they would much rather not have to see.
Technically speaking C# allows custom scheduler implementations, and internally ThreadPool is an abstraction too - e.g. AOT will use kernel-provided threadpool on Windows for size savings by default.
Practically speaking, however, you are right.
Coming from Swift, I think the same. It’s good to have the liberty to be able to do anything, but a good way to do most of the things is primordial too.