Micro-libraries need to die already
The article critiques micro-libraries for introducing complexity and security risks, advocating for developers to use simple utility functions instead, which can reduce dependency issues and improve code efficiency.
Read original articlethe safety of established libraries, even when they may not be the best solution. This reliance on micro-libraries can lead to unnecessary complexity and increased risk in software development. The author argues that micro-libraries often do not provide significant benefits compared to the costs they incur, such as poor fit for specific problems, potential security vulnerabilities, and bloated codebases. Instead of using these tiny libraries, developers should consider copying and pasting simple utility functions directly into their code. This approach minimizes risks associated with third-party dependencies and avoids the pitfalls of frequent updates and transitive dependencies. The article emphasizes that the programming community should rethink the use of micro-libraries and recognize that many trivial functionalities can be effectively handled with straightforward code snippets.
- Micro-libraries often introduce more problems than they solve, including security risks and performance issues.
- Copy-pasting simple utility functions can be a safer and more efficient alternative to using micro-libraries.
- The reliance on libraries may stem from a fear of making mistakes, leading to unnecessary complexity in codebases.
- Many common functionalities can be implemented with simple one-liners, negating the need for external libraries.
- Developers should critically assess the trade-offs of using libraries versus writing their own code.
Related
Programmers Should Never Trust Anyone, Not Even Themselves
Programmers are warned to stay cautious and skeptical in software development. Abstractions simplify but can fail, requiring verification and testing to mitigate risks and improve coding reliability and skills.
How the Curse of Lisp impacts your business (even if you don't use Lisp)
The Curse of Lisp highlights how languages like Lisp's self-sufficiency can hinder growth by limiting external contributions and fostering insular development practices. Balancing internal efficiency with external innovation is crucial for sustainable system design.
Why We Build Simple Software
Simplicity in software development, likened to a Toyota Corolla's reliability, is crucial. Emphasizing straightforward tools and reducing complexity enhances reliability. Prioritizing simplicity over unnecessary features offers better value and reliability.
We Build Simple Software
Simplicity in software development, likened to a Toyota Corolla's reliability, is crucial. Emphasizing straightforward tools, Pickcode aims for user-friendly experiences. Beware of complex software's pitfalls; prioritize simplicity for better value and reliability.
Microjs
Microjs showcases lightweight JavaScript micro-frameworks and libraries, emphasizing efficiency and portability. It allows developers to find and contribute to compact tools, promoting smaller, specialized libraries over larger frameworks.
Another advantage is that because they're so minimal and self-contained, they're often "completed", because they achieved what they set out to do. So there's no need to continually patch it for security updates, or at least you need to do it less often, and it's less likely that you'll be dealing with breaking changes.
The UNIX philosophy is also build on the idea of small programs, just like micro-libraries, of doing one thing and one thing well, and composing those things to make larger things.
I would argue the problem is how dependencies in general are added to projects, which the blog author pointed out with left-pad. Copy-paste works, but I would argue the best way is to fork the libraries and add submodules to your project. Then if you want to pull a new version of the library, you can update the fork and review the changes. It's an explicit approach to managing it that can prevent a lot of pitfalls like malicious actors, breaking changes leading to bugs, etc.
To reformulate the statement made in the intro of this post: "maybe it’s not a great idea to outsource _any critical_ functionality to random people on the internet."
It has long been a standard, best practice in software engineering to ensure dependencies are stored in and made available from first-party sources. For example, this could mean maintaining an internal registry mirror that permanently stores any dependencies that are fetched. It could also be done by vendoring dependencies. The main point is to take proactive steps to ensure your dependencies will always be there when you need them, and to not blindly trust a third-party to always be there to give your dependencies to you.
If you're particularly unlucky, the unused functionality pulls in transitive dependencies of its own - and you end up with libraries in your dependency tree that your code is literally not using at all.
If you're even more unlucky, those "dead code" libraries will install their own event handlers or timers during load or will be picked up by some framework autodiscovery mechanism - and will actually execute some code at runtime, just not any code that provides anything useful to the project. I think an apt name for this would be "undead code". (The examples I have seem were from java frameworks like Spring and from webapps with too many autowired request filters, so I do hope that is no such an issue in JS yet)
- Documentation: they are usually well documented, at least a lot better than your average internal piece of code.
- Portability: you learn it once and can use it in many projects, a lot easier than potentially copy/pasting a bunch of files from project to project (I used to do that and ugh what a nightmare it became!).
- Semi-standard: everyone in the team is on the same page about how something works. This works on top of the previous two TBF, but is distinct as well e.g. if you use Axios, 50% of front-end devs will already know how to use it (edit: removed express since it's arguably not micro though).
- Plugins: now with a single "source" other parties or yourself can also write plugins that will work well together. You don't need to do it all yourself.
- Bugs! When there are bugs, now you have two distinct "entities" that have strong motivation to fix the bugs: you+your company, and the dev/company supporting the project. Linus's eyeballs and all (yes, this has a negative side, but those are also covered in the cons in the article already!).
- Bugs 2: when you happen upon a bug, a 3rd party might've already found a bug and fixed it or offered an alternative solution! In fact I just did that today [1]
That said, I do have some projects where I explicitly recommend to copy/paste the code straight into your project, e.g. https://www.npmjs.com/package/nocolor (you can still install it though).
[1] https://github.com/umami-software/node/issues/1#issuecomment...
I would prefer them to be built straight in the languages.
I fail to comprehend how a single-function-library called "isNumber" even needs updating, much less "fairly frequently".
The debate around third-party code vs. self-developed is eternal. IMHO if you think you can do better than existing solutions for your use-case, then self-developed is the obvious choice. If you don't, then use third-party. This of course says a lot about those who need to rely on trivial libraries.
surely it can't be beyond the wit of programming kind to have a standard lib, or even layers of standard lib for Node?
What is the argument for not having a standard lib, apart from download speed?
This doesn't apply to micro-libraries, but it looks like that cost/benefit list is intended to cover libraries in general.
Well, that's a proper use of SemVer, not sure why you put it against the library's author. I've personally been burned enough times by libraries that for some reason think that literally being unable to compile them is somehow a backwards-compatible change, so it's refreshing to see that some people actually understand that.
Normally, packages are listed in my composer.json and stored in vendor/. For those packages, I created a separate folder called vendor_private/ which is part of my Git tree, put copies of these weird little packages in it, and set up my composer.json to consider that folder a repository.
Works like a charm. My big important packages are still upstream. I can customize the little ones as needed to fit better, or have better code, and not worry about them going unmaintained. It’s also way quicker than copying the files individually out of the package and into the right places (along with updating Namespaces, configuration, etc.) Once in a while, I’ll go back and see if anything worthwhile has changed upstream - and so far, it never has.
I'm also an advocate, against crowd, of qualified imports as they help with refactoring (renames are propagated, especially in monorepos), readability/reviews (functions are qualified, you know where they're coming from) and overall coding experience – qualified module name followed by dot gives good autocompletion, imports look neat in larger projects etc. The codebase written like this resembles extended standard library. It also helps with solving problems by encouraging first principle thinking, bottom up coding that produces auditable codebase with shallow external dependencies etc.
Using SNS as an example when it's neither micro nor a library but a service (and a huge abstraction over native push notifications, whereas most micro-libraries provide simple utilities that aren't very abstract), saying that complex libraries are harder to audit and hence a security risk (which should be a point in favor of micro-libraries that are small enough to audit in minutes), saying libraries might have large footprints (which is surely another reason to go for micro-libraries over all-you-could-possibly-need-libraries), saying transitive dependencies are bad, (yet again, this points towards an advantage of micro-libraries, which are less likely to have many dependencies), ... I don't know.
"Would future updates be useful? No. The library is so simple that any change to the logic would be breaking, and it is already clear that there are no bugs."
Maybe what you want is a library ecosystem where things can be marked "this will never change". Something crazy happens and you actually need to update "is-number"? Rename it.
Of course, you can simulate that with a single large omnibus dependency that everyone can trust that pulls all these silly micro-libraries in verbatim.
Indeed you can, but it depends what isNumber does. This is more like what it should do IMO:
function isNumber( foo ) { return ( (typeof foo === "number") && (foo == foo)) || ((typeof foo === 'object') && (foo instanceof Number) ); }
And that is I think the value of micro libs, at least in JS, you don't want to think about all the edge cases when you only want to check if something is a Number.
However, if I can inline a small function, I will, so in that sense I agree.
And if the LLM ain't good enough to write leftpad, how can I trust it to write anything at all?
Perhaps we should start there.
Obviously you want basic, stable and well documented functionality in your programming language.
But JavaScript does simply not have it. So how do you solve this dilemma?
1) the everything is an import way: use NPM and create a dependency hell from hell (requires Satan) made by Lucifer (same as Satan but different) using lava with fire (requires node v <= 9.42.0815) and heat (deprecated) requiring brimstone (only node v > 10.23) with a cyclic dependency on the Devil (incompatible with Satan).
2) the Golang way: copy paste ALL the things, only for your co worker to copy paste all the things again, only for your co worker to copy paste all the tings again, only for your...
Way 1 wastes your time when it breaks (sooner than later) but is necessary for non trivial functionality. Way 2 works only for trivial packages so choose your poison.
JavaScript (apart from not being a good programming language in general) is sorely missing a std lib.
One could argue that having a bad std lib is even even worse (PHP anyone?) but it is really hard to decide.
Sadly JavaScript is just unfit for the purpose it is being used for.
Applications should never have trivial, tiny libraries as moving-target external dependencies.
If you must use a small library, bring it into the program.
The advantage: - everybody can contribute an npm package
The disadvantage: - everybody can contribute an npm package
Passive voice. WHO should never use micro-libraries?
How is this the fault of the library? You chose the wrong one!
"This often cancels out the primary benefit of libraries. No, you don’t have to write the code, but you do have to adapt your problem to fit the library"
You evaluated the library, found is unsuitable and yet, it is somehow their fault.
Why on earth would you project your own failures on to someone else's code? You do you!
> I have talked a lot about the costs of libraries, and I do hope people are more cautious about them. But there’s one factor I left out from my previous discussion. I think there’s one more reason why people use libraries: fear.
> Programmers are afraid of causing bugs. Afraid of making mistakes. Afraid of missing edge cases. Afraid that they won’t be able to understand how things work. In their fear they fall back on libraries. “Thank goodness someone else has solved the problem; surely I never would have been able to.”
I think this is true, but why does the JS ecosystem seem to have "more fear" than for example the Python ecosystem?
I wrote about this a while ago. I think that actually JS does (or did) cause more fear in its developers than other programming languages. I described it as paranoia, a more insidious uncertainty.
Quoting myself[1]:
> There are probably many contributing factors that have shaped NPM into what it is today. However, I assert that the underlying reason for the bizarre profusion of tiny, absurd-seeming one-liner packages on NPM is paranoia, caused by a unique combination of factors.
> Three factors have caused a widespread cultural paranoia among JavaScript developers. This has been inculcated over years. These factors are: JavaScript's weak dynamic type system; the diversity of runtimes JavaScript targets; and the physics of deploying software on the web.
...
> Over the years there has been rapid evolution in both frontend frameworks and backend JavaScript, high turnover in bundlers and best-practises. This has metastasized into a culture of uncertainty, an air of paranoia, and an extreme profusion of small packages. Reinventing the wheel can sometimes be good - but would you really bother doing it if you had to learn all the arcane bullshit of browser evolution, IE8 compatibility, implementation bugs, etc. ad infinitum?
> And it's not just that you don't understand how things work now, or how they used to work - but that they'll change in the future!
[1] https://listed.to/@crabmusket/14061/javascript-s-ecosystem-i...
Related
Programmers Should Never Trust Anyone, Not Even Themselves
Programmers are warned to stay cautious and skeptical in software development. Abstractions simplify but can fail, requiring verification and testing to mitigate risks and improve coding reliability and skills.
How the Curse of Lisp impacts your business (even if you don't use Lisp)
The Curse of Lisp highlights how languages like Lisp's self-sufficiency can hinder growth by limiting external contributions and fostering insular development practices. Balancing internal efficiency with external innovation is crucial for sustainable system design.
Why We Build Simple Software
Simplicity in software development, likened to a Toyota Corolla's reliability, is crucial. Emphasizing straightforward tools and reducing complexity enhances reliability. Prioritizing simplicity over unnecessary features offers better value and reliability.
We Build Simple Software
Simplicity in software development, likened to a Toyota Corolla's reliability, is crucial. Emphasizing straightforward tools, Pickcode aims for user-friendly experiences. Beware of complex software's pitfalls; prioritize simplicity for better value and reliability.
Microjs
Microjs showcases lightweight JavaScript micro-frameworks and libraries, emphasizing efficiency and portability. It allows developers to find and contribute to compact tools, promoting smaller, specialized libraries over larger frameworks.