
Never use a dependency that you could replace with an afternoon of programming - earthboundkid
https://blog.carlmjohnson.net/post/2020/avoid-dependencies/
======
jdmichal
My rule is, don't use a dependency to implement your core business. Is JSON
parsing our core business? No, so why would we ever write -- and thereby
commit to supporting for its entire lifetime -- JSON parsing code? All the
code you write and support should be directly tied to what you as a business
decide are your fundamental value propositions. Everything else you write is
just fat waiting to be cut by someone who knows how to write a business case.

To be clear, this is about the _lifetime support_ of code. It's very, very
rare that code can be written once and never touched. But that long tail of
support eats up time and money, and is almost always discounted in these
conversations. I don't even care that Jackson JSON parsing has years of work
behind it, when I can hack together a JSON parser in a day. I care that
Jackson will _continue_ to improve their offering without any further input,
while that's not true of my version.

~~~
nayuki
> don't use a dependency to implement your core business

In logic language, you're saying "If X is your core business, don't outsource
X".

> Is JSON parsing our core business? No, so why would we ever write -- and
> thereby commit to supporting for its entire lifetime -- JSON parsing code?
> All the code you write and support should be directly tied to what you as a
> business decide are your fundamental value propositions. Everything else you
> write is just fat waiting to be cut by someone who knows how to write a
> business case.

The rest of your argument is interpreted as "If X is not your core business,
don't in-house X".

These two logical implication statements are not equivalents of each other,
but are converses. Casual language often conflates If, Only-If, and If-And-
Only-If.

~~~
soedirgo
Since we're in pedanticville, these aren't converses, but inverses. The
converse goes "If you don't outsource X, then X is your core business".

~~~
nayuki
Thanks, you are right. A converse is logically equivalent to an inverse, so
I'm only half wrong. =)

~~~
michaericalribo
Ah, yes, everyone knows the three values of Boolean logic...

~~~
zeeZ
true, false and null

~~~
mstade
Actually it's True, False, and FileNotFound. :o)

[https://thedailywtf.com/articles/What_Is_Truth_0x3f_](https://thedailywtf.com/articles/What_Is_Truth_0x3f_)

------
sparker72678
Probably good advice, but when was the last time a programmer accurately
scoped a problem when they said it will take “an afternoon” to build?

~~~
andrewstuart
I came here just to say this.

"This'll take an afternoon" \- three weeks later......

Programmers are notorious for this.

BUT even apart from this problem ... you absolutely should use every
dependency you can that will save you time.

Try to write less code not more. When you write code you write bugs, add
complexity, add scope increase need for testing, increase the cognitive load
required to comprehend the software, introduce the need for documentation.....
there's a vast array of reason to use existing code even if you truly could
estimate it and build it in an afternoon.

You also assume that you understand all the edge cases and fickle aspects of
the dependency, all the weird ins and outs that the dependency author probably
spent much resources understanding, fixing and bug hunting.

There's a hard fact that proves the above poster to be wrong..... how many
dependencies took only an afternoon of time in total to write? Hard to say
(maybe look at the github commit history) but I'd guess almost none. It didn't
take the dependency author an afternoon, so why will it take you an afternoon?

Even worse .... you just lost an afternoon coding features for your core
application.

Multiply this by every dependency that "you could build in an afternoon" and
you'll be in Duke Nukem Forever territory.

I'd advise doing the opposite of this articles suggestion.

Find a dependency that will save you an afternoon? Grab it.

~~~
VHRanger
Found the NPM user.

Dependencies have costs:

\- Dependencies break over time. They have a nonzero maintenance cost.

\- They impose API boundaries on you that may not fit your existing data
structures

\- It's harder to change underlying bugs

\- They might introduce security issues

Sure, use dependencies. But there's a reasonable position between "never write
any code" and "never take on dependencies". Of which NPM is one of the only
ecosystems being at one extreme.

~~~
scarface74
I could say all of the same things about the in house tools that the
“architect” wrote three jobs ago - including the bespoke ORM, object mapper,
and logging framework.

Or two jobs ago where two developers who had worked at the company for 10 and
15 years respectively were maintaining a bespoke 15 year old EHR written in
PowerBuilder and depended on SQL Server 2003 - in 2016.

Every company thinks they are their own special snowflake where cross cutting
concerns can’t be handled by a third party.

~~~
JackFr
I’ve worked with dozens of guys who loved to reinvent config and logging
frameworks, because ... I don’t know why.

I suppose because it was easier than working in the actual problem domain,
which they knew little about and didn’t care to learn.

~~~
nothal
Herding devs to work on the boring-useful things instead of the interesting-
solved things is slightly easier than herding cats.

------
idreyn
A year ago I needed a min-heap to build a priority queue at work.

So first I grabbed 'heap' from npm (272k weekly downloads) and set it to work.
But a few days later I realized my code was executing slower than expected
because it sometimes needed to clone the data structure, and the clone
instantiation would break the heap invariant in the array internals. It turned
out there's been an issue open about this since early 2017.

Then I went for the 'collections' package (35k weekly downloads) and brought
in its heap implementation. That worked like a charm for about six months
until a bug came in that made it seem like a completely different package was
breaking. After almost a whole day of debugging, it turns out that
'collections' silently shims the global Array.from function (scream emoji)
without mimicking its behavior when dealing with non-Array iterables presented
by the other package.

So finally I wrote my own heap -- well, I cribbed from Eloquent JavaScript [0]
but I did have to briefly remember a little bit about how they're supposed to
work. So while I don't totally buy the "Never..." rule in the post title,
thinking more carefully about writing versus importing a dependency would have
saved me a great deal of headache in this case.

[0]
[https://eloquentjavascript.net/1st_edition/appendix2.html](https://eloquentjavascript.net/1st_edition/appendix2.html)

~~~
newen
Wow. Could not be more glad I’m not in the javascript world.

------
earthboundkid
OP here: A lot of people are objecting, "What if you estimate wrong, and it
takes more than an afternoon?" This objection is very bad.

It is not possible to add a new dependency in less than afternoon because you
need to evaluate alternatives, test it, learn how it works, make sure it's not
accidentally GPL, etc. So there are not two methods, the less-than-an-
afternoon method and the more-than-an-afternoon method. There are two methods
that both take at least one afternoon. If you estimate wrong and you can't
write the code in an afternoon… Then stop working on your handwritten version
and find a dependency? But now you know what the dependency is really supposed
to do and why it's non-trivial, so you're in an even better position to
evaluate which ones are good.

~~~
nullc
> But now you know what the dependency is really supposed to do and why it's
> non-trivial, so you're in an even better position to evaluate which ones are
> good.

I came in here to say this. If you think you're not qualified to write the
function, you're probably also equally unqualified to choose someone else's
implementation of it.

There is a lot of stuff out there-- stuff which is widely used-- which is not
fit for your purposes, ... perhaps not for anyone's. And there is no
replacement for a bit of domain expertise.

~~~
Shank
Not a lot of people can correctly write cryptography code on the first try,
but we definitely advocate for people pulling well known cryptography
libraries and using them instead of building their own, for obvious reasons.
Not many people are qualified to write a lot of things, but are capable of
making sound dependency judgements with heuristics. The trick is to use good
heuristics and to not use a library for every tiny thing.

~~~
nullc
It's my experience that people very often do not make sound dependency
judgements on cryptography dependencies.

They probably do better than writing it on their own, but that isn't
necessarily saying much-- and I think the difference isn't actually that great
(essentially the thing they pick will often tend to have same flaws as what
they would have written, because essentially we're drawing from the same
distribution).

I agree that heuristics could help but not much time is spent discovering and
socializing what those are, particularly to the extent that they are domain
specific.

Naive heuristics can also backfire. E.g. it can be easy to mistake contentious
behaviour with flaws and end up preferring code that has absolutely zero
mitigations against an attack over code that discloses the limitations of
their mitigations.

------
jedberg
This sounds like pre-mature optimization. If the library does what you need it
to do, use it. If it becomes a problem later, _then_ optimize it.

That last thing you want to do is spend a bunch of time reimplementing code
when: 1) It may not matter at all, 2) You might miss important edge cases, or
3) You got everything right but you still have to maintain it forever.

If it's going to take you three days to integrate the library, maybe it's not
such a good library, or maybe it's really complicated because there are a lot
of edge cases. In that case, dig into the code and see if you can figure out
what it's doing.

But if you think you can spend an afternoon rewriting a library that would
take three days to integrate, there is a good chance you might be missing
something important.

~~~
lolc
I remember starting with an overloaded library from NPM where I used some
basic functionality. That worked fine for a while. When later I got lost
fixing a defect in the tangle, I just ripped out the parts I needed and made a
trimmed version. The interface remained the same, for the parts I was using.
Nothing to adapt.

In this way I had little investment in the beginning. And once I knew what I
needed, it was another small investment to clean the code.

------
lordnacho
It's a matter of judgement, but here's a few observations:

\- With a little experience, you know what gets fiddly and what doesn't. Today
for instance, I needed a way to remove tags in an SVG document, which looks a
lot like HTML tags. I quickly ended up finding that Regex is not the solution
(a well known guy on SO wrote an answer that looks like a huge warning sign).
I also couldn't enumerate all the corner cases. So I found a lib that does it,
along with an SO answer that turns it into a two-liner.

\- Dependencies vary in quality. Some are basically like another standard lib.
Boost for instance is very well used. The tough ones are where the lib seems
to be "finished", where there seem to be few commits recently, but the project
was once lively and functional. IIRC libev comes to mind here. And then there
are the totally dead projects, where there's a load of issues open and nobody
saying anything.

\- Try to lock down versions. If you get a thing working with a certain
version, there's no reason you need the newest new as soon as it's pushed. You
can probably live with doing a scan for updates now and again.

\- Your afternoon of programming needs to have a clear end. That hashmap you
wrote will very likely spew out issues over the next few days. CSV parser,
maybe. Bessel function, that'll work.

~~~
JadeNB
> a well known guy on SO wrote an answer that looks like a huge warning sign

For those who are in today's 10000, you might mean this piece of art:
[https://stackoverflow.com/questions/1732348/regex-match-
open...](https://stackoverflow.com/questions/1732348/regex-match-open-tags-
except-xhtml-self-contained-tags/1732454#1732454)

~~~
lordnacho
That's exactly it! Luckily I hadn't started coding when I found it.

~~~
yoz-y
The old classic. I still end up using regexes when I need to clean up HTML
because even though it's not the right way, it's the way that is sufficient
95% of the time.

------
mcqueenjordan
Avoiding dependencies is a noble goal, and something to be valued, but this
simple rule is too simplistic.

The problem lies in the fact that there are a great many things I can hack
together in an afternoon to "replace" some kind of external dependency, but
the quality discrepancy of these hacks is highly variant. My understanding of
what can or should be done in an afternoon might differ with my colleagues'.

Unfortunately, like all things in engineering, you have to carefully reason
about the pros/cons, requirements, and costs. After that analysis, you can
make a judgment on depend vs. build (also, buy vs. build).

~~~
lgessler
Agreed. For libs that are "afternoon-y" in their scope (so, not an HTTP server
or crypto), if you need to get off the fence you can use some cheap heuristics
to assess the quality of a library without auditing its code. For instance,
you can look at its popularity (in downloads or Github stars), its
release/version history, its number of open issues, and its development
activity. If I see high issue counts and many major releases with breaking
changes, I'm going to avoid it. If I see 2+ years of stability with mostly
minor releases, low issue counts, and high use rates, I figure it's going to
probably be better than whatever half-baked solution I could scribble in an
afternoon.

~~~
kmike84
I wouldn't consider a high number of open issues a problem on its own. All big
popular projects with a history have a high number of open issues. There are
some exceptions, who may be closing isses aggressvely, but it is more about a
style of managing of those issues, not about project health.

Over time an issue tracker inevitably becomes a collection of hard-to-
reproduce bugs, incomplete patches, underspecified feature requests, random
tracebacks, etc. Maintainers can choose to just close everything which is not
actionable immediately, or be in comfort with such issues, and let them live
in the bug tracker. I personally like a style when an issue is closed only if
it is fixed, or if it doesn't contain useful information, or if it is a
duplicate.

A better indicator is activity and responsiveness of the maintainers in the
issue tracker.

------
BenoitEssiambre
Carmack weights in:
[https://twitter.com/ID_AA_Carmack/status/1293311943995002881](https://twitter.com/ID_AA_Carmack/status/1293311943995002881)

"This HN discussion
[https://news.ycombinator.com/item?id=24123878](https://news.ycombinator.com/item?id=24123878)
is topical for me: at this very moment, I am implementing C++ MFCC code
myself, because my attempt to integrate Kaldi (on windows) was unpleasant. It
already took more than an afternoon, but I learned good things! \ I'm more
sympathetic to the Use-All-The-Dependencies crowd than some might suppose. It
definitely isn't my way, but I see them as a fellow subclass of programmer,
evolved for other environments. It is _amazing_ what can be cobbled together
in a weekend now. \ The old Knuth vs McIlroy story is relevant:
[http://leancrew.com/all-this/2011/12/more-shell-less-
egg/](http://leancrew.com/all-this/2011/12/more-shell-less-egg/)

Generally, use-the-tools is correct, but sometimes you really do want a Knuth
(or maybe a Carmack)." "

~~~
andrewstuart
When Carmack speaks it's worth listening to.

However in almost all cases, you - the reader - cannot achieve in an afternoon
what Carmack can achieve in an afternoon.

Few developers have the ability to build an alternative to a speech
recognition dependency.

------
rurp
> Many times, the best approach is first searching online and reading the code
> to a few other solutions, then writing your own with the knowledge you’ve
> gained from seeing how they work.

I think this is a great approach. Any general solution library will be much
larger and more complex than one project's use case. Seeing how other people
have solved a given problem can give a dev a nice jump start on creating a
compact solution to their given problem.

For whatever reason many devs just _love_ using dependencies and almost always
underestimate the long term costs, in terms of maintenance and vulnerability
risk.

I once had to write a simple utility script that was literally 5 lines of
Python. All it did was loop through a list and perform a couple simple
actions. As I mentioned it to the PM, another dev jumped in and said, "Oh hey,
there's Library_X that we can install that will totally handle that for you".
I responded that the library could very well be great, but script was already
done, had only taken about 15 minutes to write, and would be very easy to
reason about and update down the line. Reading about and implementing a whole
new library to do the same thing just didn't seem useful.

The other dev sounded unconvinced.

~~~
alephu5
In my experience this is quite typical in the python community, I think it's a
consequence of the batteries-included philosophy. Many people are very averse
to using primitive data structures and operations to solve their problems, so
working with a python codebase is like gluing libraries together

------
ChrisMarshallNY
I tend to avoid dependencies, if at all possible. I wouldn't mind spending a
week of programming to avoid some dependencies.

Of course, there is no "one size fits all" rule, here. If it's a "one-off"
internal tool, without much impact, then it would not be worth spending much
time on, and a dependency might be exactly the right thing (famous last
words).

I'm pretty obsessive about quality, and like to ensure the best quality
possible in all my work. The weakest link, and all that, so dependencies need
to be vetted _very_ carefully.

There's some things that just can't be done without dependencies; sometimes,
crappy ones (like SDKs), but that's less frequent than you'd think.

~~~
therealdrag0
That's interesting. Kudos for your craftsmanship.

IME those who write code instead of using a dependency are those who DON'T
seem to care about quality. They can't be bothered to see if the problem is
already solved. They can't be bothered to design a good interface and extract
the code into a re-usable module. Just just solve their own immediate problem,
which then gets re-solved in different ways a dozen times in different
projects in the company. This is what I find frustrating and what I find 3rd
party dependencies protect against. They define a set of culturally
accepted/known APIs/Functionality that is consistent and doesn't have to be
relearned between repositories.

~~~
ChrisMarshallNY
In my experience, I have seen some Jurassic-scale disasters, because of poor
dependency choices.

I think a lot of people just google for dependencies, and then add the first
one that has a slick Web site, without thinking much about the code they are
adding.

I am not a "never dependency" person, but I am _anal_ about quality. Totally
obsessed. I feel that quality is something that many, many programmers eschew,
in favor of bling and buzzwords.

For me, I won't put my seal on something until I have tested it six ways to
Sunday. In some cases, it may be unit tests, but, more often, it is a test
harness, which can be a much more ambitious project than a few XCTests[0]. In
fact, I am running into a lot of folks that don't know what a test harness is;
which is jaw-dropping.

Since I do a lot of device control stuff, unit tests are not particularly
useful. In order to create unit tests that would be effective, I'd need to
design a huge mock, and that would not be worth it; likely introducing more
problems than it solves.

An example is that I am currently developing a cross [Apple] platform
Bluetooth LE Central abstraction driver[1]. This needs to have test harnesses
in all the target systems (iOS/iPadOS, MacOS, WatchOS and TVOS). I actually
have it driving a released app[2] (which is really just a "productized"
implementation of the iOS test harness), but I do not consider the module
ready for release, as I have not completed all of the test harnesses. I am in
the middle of the WatchOS harness now. I may "productize" the MacOS test
harness. My test harnesses are really serious bits of code. Complete, ready-
to-ship apps, for the most part. Going from a test harness to a shipping app
isn't a big deal.

[0] [https://medium.com/chrismarshallny/testing-harness-vs-
unit-4...](https://medium.com/chrismarshallny/testing-harness-vs-
unit-498766c499aa)

[1]
[https://github.com/RiftValleySoftware/RVS_BlueThoth](https://github.com/RiftValleySoftware/RVS_BlueThoth)

[2] [https://apps.apple.com/us/app/blue-van-
clef/id1511428132](https://apps.apple.com/us/app/blue-van-clef/id1511428132)

------
aziytuiam
A hundred afternoons later my small application is finally completed; now I
must maintain update and document it all for ever rather than relying on third
party components. What I really wish is people would look closer to home; for
example use some of the thousands of functions that ship with your operating
system before downloading a package.

~~~
vorticalbox
I have this problem at work with our node apps.

With things like reduce, map, the like their is less need to rely on lodash or
underscore for them.

~~~
fastball
The problem with node is that it doesn't ship with a stdlib.

That being said, lodash has worked great for utility stuff. It encapsulates
like 99% of utility functions I need in my apps.

------
scandox
Frankly my own code is usually the worst dependency I could have.

~~~
thrownaway954
so true :(

------
orthecreedence
Even if this is true, the problem is you don't know what's going to take an
afternoon of programming. How many times have we all said "should only take an
hour" just for it to take four days?

If someone spent the time and effort building something you need, I don't see
a problem with using it. It all depends on what kind of system you're
building, what the security and stability guarantees are, and in general what
trade-offs you want to make.

In other words, "never" is usually bad advice.

------
moron4hire
Especially when it comes to JavaScript, I tend to follow "never use a
dependency that you could replace in a _month_ ".

Here is something that has happened to me more times than I care to count: I
have a dependency on some library. It's working well for me. I build some sort
of small project with it, like the info website for my mother's business.
Doesn't need to be updated very often, just needs to be there and have her
contact information. A year later, I get some automated email telling me some
dependency of a dependency of my dependency has some kind of obscure security
issue. I'm busy. This project isn't supposed to be a core, constantly making
project. I don't really have time to evaluate if the problem is an issue for
me, so I go to just upgrade everything and appease the squawk box. Oh, turns
out that in the time between when I used the library and the security issue
was discovered, the developers have completely redesigned the whole thing and
the only version that includes the security fix is a version that isn't
compatible for my config scripts anymore.

All because I didn't want to spend the time to write some stupid simple string
concatenating code for writing an HTML template, I now have to spend time
completely relearning this library. And probably rebuilding the build scripts,
too, because nobody can just leave well enough alone.

"Oh, but if you wrote the code, you might have written a security bug, too".
And I could fix it on my own, too.

When did programmers stop programming? Most of the arguments I see for
ridiculous levels of package integration come from a place of devs not
trusting themselves to write stupid simple code. Like left-pad. "Someone
smarter than me has figured at all the edge cases". You don't need all the
edge cases. You need to just learn how to do your job.

~~~
AtlasBarfed
JavaScript, capitalizing the W in "worse is better".

------
whalesalad
So true. Oftentimes I will look at the source of a simple ruby gem or node
package and see that it is actually really just one single file, with 100
various .yml/spec/lint/test/cloud/coverage/cov/tox/blah landmines in the repo
to confuse you. In those cases, I'll copy-paste the code with attribution back
at the top of my source file.

The headache is not worth all of the pomp and circumstance for some of these
tiny little tools.

------
MrStonedOne
Dependencies are part of the same calculations that normal programming
follows.

Easy to program vs Easy to maintain vs Easy to understand vs Easy to scale vs
Easy for the cpu to do.

Dependencies are easy to program, you include them.

Dependencies are hard to maintain, in that you can't expand or change their
behavior.

Dependencies can be either hard or easy to understand (compared to your own
code).

Dependencies are hard to scale, something you coded yourself can avoid
validating inputs that have already been validated, or otherwise be smarter
about how it does things to avoid overhead.

(one example would be a system for parsing info from a large amount of data,
most dependencies will make you either store it all in one place on disk or
memory, where a custom solution can more easily stream it to avoid memory or
disk overhead. (take a ftp server that wants to calculate and store 5 hashes
for each of the files uploaded, it can either have 5 libraries each read the
file after upload, or it can load the entire file back into memory after
upload or it could just stream the info to 5 custom rolled hashing systems
that handle calculating the data _as its being uploaded_ and written without
needing to re-read it from disk after upload. one will be faster and scale
better.))

So half the articles about dependencies on hacker news can be broken down to
one message:

Don't use a dependency when rolling it yourself is better for your use case.

The other half are about conveying why and when rolling it yourself can give
you benefits for your use case.

------
ChicagoDave
I've always called this the "guy in the basement" effect.

In the beginning, the guy in the basement would have an awesome idea for a
library. He would spend 27 hours a designing it, coding it, testing it, and
making it awesome.

The "guy in the basement" moves out because his partner wants to spend time
with him. They start to live lives outside of the world of maintaining open
source libraries. The partner asks, "Are you getting paid for all of this?"
and the guy says, "No. It's open source. It's a good thing." but in saying it
out loud to his partner, his priorities shift.

Over the next year, the library deteriorates. It gets forked, other people ask
to maintain it, he relents, but no single person has the vision. The library
becomes a mish mash of priorities and no longer has the awesome cohesion it
once had.

Everyone using the library is very sad. Some are angry. An awesome replacement
appears from another guy in the basement.

The cycle repeats.

------
mcphage
(1) Most programs don't have that many _direct_ dependencies, especially
smaller dependencies. It's often dependencies of dependencies. In npm, adding
just `jest` will make your `node_modules` directory explode.

(2) if Linus's Law is "given enough eyeballs, all bugs are shallow", then
having fewer eyes on a library means more bugs.

(3) Oftentimes you _think_ you can replace a dependency with an afternoon of
programming, but it turns out, it's not quite as simple as you think.

(4) Sometimes you only use a small piece of a library to start with, but over
time use more and more. If it's your own code, then you're going to be
continuously refactoring, updating, rewriting it. If it's a library, then you
can just start using the additional pieces as you need.

------
closeparen
The single best way to avoid dependencies is to use a language with a large
standard library.

Given the variance in standard library coverage, it’s rarely productive to
argue about this topic in a language agnostic way. Using only stdlib in Go is
very different from using only stdlib in JavaScript.

~~~
smabie
The best way to avoid dependencies is to use a language that is built in a way
such that dependencies are worthless. Like APL, J, or kdb+/q. All of these
languages are incredibly small, have almost non-existent standard libraries,
and yet are designed in such a way that a large standard library becomes
superfluous.

Having a large standard library speaks poorly of the composability and
orthogonality of language primitives.

~~~
yoz-y
Sure, if the problems you are solving are well suited for languages like APL,
J or kdb+/q...

~~~
smabie
Call me an iconoclast, but I think array languages are more suitable for
general purpose programming than even general purpose languages.

~~~
rrampage
As someone who is not well versed in APL/J/K, how do they deal with structured
data like JSON? Is there some sort of DSL like jq built into them?

~~~
smabie
Because kdb+/q has such few datatypes and you can't define anymore, it's very
easy:

    
    
      q) .j.k"[2.2, 3.5]"
      2.2 3.5
    

Serialization is also trivial:

    
    
      q) .j.j 0 1 2 3
      "[0,1,2,3]"
    

In general, sending stuff across memory boundaries (files, network, ram, etc)
is exceedingly trivial in kdb+/q. To execute a function on a remote server,
simply connect to a server and send across the call to the handle. For
example, to send synchronously compute 1+1:

    
    
      q) h:hopen `::6666
      q) h(+;1;1)
      2
    

You can send over anything you want, even the entire source code of program to
be executed! This is a really flexible environment, where you can create
really powerful app-engines. All members of a cluster can send code, data, and
messages to any other node, async or sync.

Much like Common Lisp and SmallTalk, you can easily connect to production
nodes and modify code while the service is running.

It's rare to find such a dynamic, flexible, and interpreted language that also
has world class performance, even often beating hand written C. Combined with
an integrated database, you get a distributed system that can't be beat, at
least performance-wise. And the craziest thing is that all of this sits in at
650kb executable with libc has the only dependency. And all probably in less
lines of code than a simple javascript webapp!

------
moltar
Never use store-bought bricks if you can replace them with an afternoon of
brick making.

[https://youtu.be/D59v74k5flU](https://youtu.be/D59v74k5flU)

~~~
jamil7
I knew which video it would be before clicking... But to your point they're
often not really "store-bought" bricks though. More like bricks someone was
giving away on the side of the road, you're free to use them to build your
house but with no guarantees they work and the instructions are missing or
incomplete. Oh and they're the wrong shaped brick but you only figure that out
later.

~~~
astura
What sorts of dependencies are you guys adding to your projects? I've never
had these sorts of issues with external dependencies.

~~~
jamil7
Ah I'm mostly joking. I haven't done much js work lately but sometimes wading
through npm did feel like the above.

------
jjav
Adding a dependency is like adopting a puppy. Sure it seems free.. but it's
not. It may be the best thing to do, but go in with eyes open on the costs.

Something like left-pad, absolutely never worth it. Something like OpenSSL,
basically always worth it (warts and all). Everything in the middle, evaluate
carefully.

When adopting a dependency you're adopting all their development practices. Do
they break backward compatibility every minor release because compatibility is
for the boring people? Never address CVEs? Every additional dependency
constrains your future self by a little bit. These add up.

------
jaeming
I once decided to write my own UI pagination component. I thought it would be
fun and, why bring in a library for something so simple? Three years later, we
now use this component for all our pagination. Probably four of us know it
really well by now as it turns out, there have been many bugs around it. Turns
out pagination was not such a trivial problem to solve when you consider all
the edge-cases and crazy PM requirements.

Every once and a while some engineer who's fixing a bug on it will ask, "why
in the world did we write this? Why don't we just use this bootstrap
pagination instead?". I ask in return, "would it fix the bug you currently
have with it?". Every single time the answer has been no. It's almost always
centered around the state of how the parent component is using it. I wrote
with the intention of using querystrings to store pagination state in the url,
but someone else at some point decides to store their pagination data in
memory and just adds a few props and now it's doing it two different ways,
etc...

I think the criminal offense here was not writing pagination from scratch. It
was that I didn't extract it into a library and write some basic
documentation.

~~~
akvadrako
It does sound like you’re agreeing with the premise of the article.

Pagination is hard but it’s even harder to find a library that meets all your
requirements and doesn’t have bugs.

------
Justsignedup
I'm the opposite. I always think I can solve it in an afternoon and always
realize there are a lot of intricacies.

Fuck making popup positioning in browsers. That shit is hard to get just
right.

~~~
dheera
Ugh please just don't use popups at all. I block every single one I see with
custom CSS rules. GDPR cookie warnings, "please subscribe", stupid "can i help
you" chat popups, everything.

------
renewiltord
I recall using an unusual library a while ago where the author had embedded
their own log4j-style implementation. I couldn't configure that the same way I
configured other logging.

Hardly a problem today since I just write to `stderr` and have the container
log aggregator push elsewhere but boy was it annoying.

------
loopz
When coding, be prepared to put it in the garbage bin! If you're prepared for
this, you can code things more quickly, and not worry about the code slowing
you down later. This works when you're unsure exactly what to build and need
to iterate (agile). First build is an iteration, not arbitrary "sprints" or
"increments"!

The cost of rebuilding MUST be budgeted though. If you don't have this
freedom, things are bound to suck one way or another. Then, next best thing,
build it as simple as you can, and put effort into making it composable and
pluggable. So you retain freedom to swap out components. This is also an
investment, and takes some more time and effort.

If you even can't even have that, results are bounded by those restrictions.

------
bob1029
I agree with this in principle. There is nuance, but the only way I look at
3rd party dependencies is as liabilities, normalizing for function.

The only question for me from this point is "Which dependencies are the
biggest liabilities for us?" The answer to this question depends wildly on
what exactly it is that you are trying to do.

Overall, our strategy is to typically use a 3rd party dependency by default in
order to quickly get a MVP rolled out. Once we have a clearer picture of how
we can solve some particular problem in a concrete way, we make a decision
regarding whether we should drop that dependency or keep it around. Most of
the time, we will develop an interface which exposes our need for the
dependency in a vendor-agnostic way. Then, we can target any arbitrary
implementation against this interface and quickly swap them around as required
in DI.

The biggest question in these discussions is always going to be "Well... how
long would it take to write our own?". Even if someone is being realistic and
gives you something 2x as long as you were hoping for, you should consider all
of the other factors. Keep in mind that if you write it yourself, you can
probably iterate on it without much difficulty as well (i.e. custom change
requests that a 3rd party would completely ignore). Conversely, if you have to
maintain it and its really buggy, you can't hope someone else is going to
eventually solve your problems for you.

------
fwip
A huge benefit of using dependencies in your codebase is that other members of
your team (including future members) have a good chance of already knowing how
the dependency works, and how to use it.

If I rewrite package Foob because it only takes me 3 hours to write (let's
call it Boof), and my colleague Mary on another team also does so, then the
chances that our needs, specific implementations & usage patterns are the
same, is pretty low. When I transfer to her team six months from now, I have
"my" code that I know how it works, and I also have "her" code that I don't
yet understand. I've got to relearn the new interface.

Or, if we see this as opportunity to unify our needs across codebases/teams,
one of our libraries get the features of the other bolted on. In this way,
we're just spending our combined time building a less-good Foob, instead of
actually doing something useful.

But instead, if Mary says that Foob is high-quality because she looked at the
code, checked the community, etc, then I can just trust her judgment. Foob
already has most the features that I thought I ain't gonna need. I can re-use
Foob in multiple projects, and the documentation for Foob is certainly better
than what I would have written in an afternoon, so future team-members will
also pick up Foob faster than my Boofy custom implementation.

~~~
foobarian
I had this experience with a particular React hook recently. We ended up with
like 5 teams implementing very similar functionality, and then when developer
#6 came along and tried to replace them all with a common implementation it
broke 2 out of 5 use cases because of very subtle edge cases. I guess React
runtime behavior is kind of high on the scale of how hard code is to reason
about, so maybe this wouldn't happen in an easier codebase. But still a very
instructive exercise.

------
tmaly
This reminds me of the Node left-pad module problem in a way. I think if
something is so trivial to write, you should write it rather than using a
dependency.

If it is non-trivial, I prefer the official standard libraries for a
programming language. That is if a solution exists in the standard library.

I think the Go standard library with its batteries included mantra and the
level of support it gets is good example of a library that should be used when
a solution exists within it or by utilizing it.

~~~
brightball
Or just C&P the relevant open source code (license allowing).

Removing a dependency doesn’t have to mean writing from scratch.

~~~
gmueckl
If you copy the code into your project you at least need to keep track of the
original authors and licensing or you're in violation of the copyright 99% of
the time.

~~~
brightball
Oh, of course. Definitely.

------
spankalee
For small dependencies it's often not the implementation that matters as much
as the tests. And even if you think you can write all the tests in an
afternoon, they're often born out of actual usage, which you can't replicate
so easily.

I'd say if a dependency is small enough that you can write it in an afternoon,
you can even more easily read it's source and tests and decide if it's high
enough quality to use as-is.

------
dkarl
I think the mistake described in this blog post needs a name. Let's call it
premature dependence.

Like premature optimization, premature dependence compounds your maintenance
burden over time because you predicted a problem which, if you had waited for
evidence, would never have materialized.

In premature optimization, you imagine that a simple solution won't perform
adequately, so you invest in building a more sophisticated solution up front.
In premature dependence, you imagine that a simple implementation won't
adequately address your needs, so you bring in a feature-rich dependency right
away.

Both are driven by the fact that we spend the vast majority of our time,
effort, and emotional energy dealing with rare cases. You don't spend hour
after hour, day after day, meeting after stressful meeting talking about the
database that scaled fine or the code you wrote once at the beginning of the
project and never touched again. If you don't account for how the successful
stuff disappears into the background, your model of reality will be skewed by
rare traumatic events. (Which is kind of the point, evolutionarily speaking,
since it serves as a kind of crude risk analysis, but it's not hard to do risk
analysis better than your amygdala does.)

Of course, applying the rule in practice requires judgment. Some problems are
inherently difficult; some explosions in complexity are predictable; you don't
have to pay long-term costs on POCs and failed experiments; if you say you
support ISO-XXXX format then you had better support every obscure corner case;
etc. But it's good to keep in mind that the long-term cost of an extra
dependency can be greater than the long-term cost of a few hundred lines of
unchanging, unit-tested code.

------
anonymousiam
What about "do not re-invent the wheel"? If you are schedule driven, then
you'll want to get the coding done ASAP. Take an action for later to reduce
dependencies, and prioritize it appropriately. Also, keep a local copy of the
version you pulled and reference that for builds so you are not at the mercy
of some maintainer/repository that has no stake in your game.

------
drchiu
I mean... this depends. I use Rails mostly in my work, and in recent years
I’ve pared down the libraries I would use (ie. that will go into the Gemfile).
I generally stay away from any other externalities beside this list of
libraries, and so I make it so that they’re almost “my own.” Seems to be a
good balance between getting too dependency dependent vs veering off to
reinvent the wheel.

------
zby
On average programmers produce average quality of software. Libraries
available online are often shitty - but most probably they are still over the
average quality of all software, because there is a selection bias and more
people are looking at them etc. So if average programmers replace average libs
- then the expected result is that the overall quality of software would go
down.

------
topicseed
Totally agreeing with this but the issue is maintenance. Sure, I can find a
package and copy and paste a few files and make it my own — or do it from
scratch even!

But then, that's not really my core domain, so I'll most likely never touch
that piece again anytime soon. A quality package tends to have that covered
(with a small risk, too if it's an untrusted source).

------
ChrisMarshallNY
I should mention that one thing that I _always_ do with dependencies, these
days, is encapsulate them.

I use things like Dependency Inversion or Dependency Injection, or simple
"glue" APIs.

This is from being burned by integrating dependencies into my projects, and
painting myself into corners.

It helps to do things like swap out DB backends, or things like mapping
libraries. YAGNI is important, but it's also important to encourage
flexibility. Dependency encapsulation is pretty much perfect for that.

Of course, there are probably cases where it's impossible to use some
dependencies without making them integral parts of the project, but it's been
my experience that I can write lots of stuff, based on modules.

My work is basically a clump of dependencies; it's just that I wrote the
dependencies.

I like modular architectures, with each module/layer as an independent-
lifecycle project; complete with testing, documentation, and APIs. Takes a bit
longer to write the modules, but the integration is quite smooth.

------
kuon
This is interesting, because I had this issue. In one java lib I wrote, I
needed to build an URL, so I naively pulled apache http common URL builder,
got it done in one line, and went on.

But a few time later, I realized (well someone reported an issue) that
depending on the android version, it would break. My first reflex was to try
to bundle just what I wanted using some graddle class renaming plugin, it was
a nightmare.

Then I just realized how stupid and lazy I had become, being able to just pull
dependency for anything made me forgot I could just write the code.

In the end I dropped the dependency and wrote what I needed myself.

If you are interested, commit here: [https://github.com/kuon/java-phoenix-
channel/commit/2a3a976d...](https://github.com/kuon/java-phoenix-
channel/commit/2a3a976d3a189d762c4eac739d7ad824e6ce6380)

------
ak39
I agree with this philosophy. I see the writer has used popular memes to
convey the problem with humour. I would also add this quote from the movie
"Heat":

 _" Don't let yourself get attached to anything you are not willing to walk
out on in 30 seconds flat if you feel the heat around the corner."_

------
sreekotay
Agree-ish - buttt makes me think: How about... dont _keep_ a dependency you
could replace with an afternoon of programming?

Factor, re-factor, and (most especially) DELETE should be tools in the toolbox
-- but see if you need it/keep it (e.g. protoype it in, etc first) before you
re-write.

~~~
avmich
> Factor, re-factor, and (most especially) DELETE

Lines of code is a passable metric for the quality of work if they are
considered "spent", not "created". That is, the programmer's work is better,
all else being equal, if it takes fewer lines of code. Who said so? Knuth,
Wirth, Dijkstra, Perlis?..

------
loup-vaillant
Those are reason why I like small "single" file libraries in C and C++ (one
header, one source file): you know where the source code is, reviewing it is
relatively painless, and adding it to your project is dead easy.

Yet I have seen people ignoring the benefits of a 2K lines dependency (in a
single compilation unit), over an otherwise equivalent 20K lines dependency
(in 100 compilation units) that requires the autotools. (The disadvantages of
a particular lightweight dependency is a separate topic.)

You want your modules do be deep: minimum interface for maximum functionality.
Dividing the size of an interface by 10 for the same functionality is a pretty
sweet deal in most cases. In my opinion, that deal is often underrated.

------
timoth3y
I agree with the spirit of this article.

However, the problem is that most of the time when we think "I could build
that in an afternoon!" we are underestimating the complexity involved.

Sometimes it takes an afternoon of coding and months of debugging.

------
ezekg
Typical programmer, wholeheartedly believing they can build a something in an
afternoon and be done with it. I don't care how well you scoped the project --
it almost always takes longer. It's naive and all this does is cost businesses
time and money. It's usually much more economical to go with a third-party
than to build everything in-house, which needs to account for the costs of
new-feature dev and long-term maintenance. I have an entire business running
on that premise.

------
fchu
That's too simplistic and not realistic.

I'd rather go for an heuristic that's the dependency management equivalent of
"Ask for forgiveness, not permission":

1.Find the best library that is good enough to solve your current needs,
double-check to understand its capabilities and shortcomings (to internalize
any known future needs), and code against it.

2\. Invest the time to build your own stuff only when you start to see
limitations in future iterations. By this time you'd also have a battle-tested
understanding of both the dependency's shortcomings and your own requirements

------
vzidex
I'm not sure if I agree - commonly-used packages will have known and
documented faults, while validating something you (your organization) created
can be challenging.

Dance with the devil you know and all that.

------
rsanek
Does it really take people "a few hours" to integrate a dependency? For
personal projects it's more like 15 minutes, and even for stuff at work it
doesn't generally take longer than an hour.

Broadly I find that FOSS libraries are great when you stick with their happy
path. If you have unique/specific requirements, it may make sense to build it
yourself. But there's no reason you can't spend an hour trying to use
something for free before re-writing from scratch.

------
kseifried
Dunning-Kreuger, the long and short of it is many problem appear "simple" and
take an afternoon.. right? For something that converts a date from ISO format
to American (M-D-Y) sure it might be simple, but anything that listens or
talks to the network (DNS? APIs? etc.) I guarantee you didn't think of all the
corner cases.

Also all those afternoons add up.

Also the comment "You’ll know exactly how it works and what its shortcomings
are."

Hahahahaha. no. If that was true bugs wouldn't be a thing.

------
luord
Controversial to say the least. While I know the value of in-housing as much
code as possible; I only think it should be done once
([https://luord.com/2016/06/25/nih](https://luord.com/2016/06/25/nih)).

After that one time, I've strove to limit the in-house code only to the actual
domain and business rules of the application/system.

------
pacifika
At lest in web development, the website is always changing thus so is the
code. Therefore the maintenance burden favours bespoke code over libraries as
the former is easier to adapt to changing requirements.

However ultimately in reality a decision based on product ownership culture,
when it’s more important to produce results now ( velocity) rather than in a
few months time (maintainability). It’s hard to change culture.

------
gspr
I'm sure I could write a basic HTTP client implementation for the current
needs of my project in an afternoon. That doesn't mean that I shouldn't be
using libcurl or whatever instead, though.

It's perfectly reasonable to conclude "my need is a subset of a larger but
well-defined task for which there's a bunch of mature, proven and widely
available libraries – I should leverage them".

------
tuyguntn
20 afternoon dependencies -> now you have month of work, more code to test
(and write tests), more code to support in the future, more code to understand
for new developer. Add edge cases which you are not aware of, you are screwed

my basic rule if dependency has small code base (<300-500 lines) I will
copy/paste that chunk of code into repo and refer original repo (assuming
LICENSE is appropriate)

------
kova12
I would actually advise another way: if you have to waste the whole afternoon
putting algorithm together, then it probably going to be a week after you
implement all tests and fix all the bugs. But even if it was just afternoon,
still not worth it. You don't want to maintain what you don't need to maintain

------
wan23
I once worked for a startup that had a custom C++ basic library for strings,
arrays, smart pointers and things like that. At some point I spent the better
part of a week looking into a bug that, long story short, was caused by a bug
in our custom String class. Please don't do this. Every line of code in your
project that your team maintains is a liability.

------
rndmze
I am biased because the ecosystem I work on most of the time has good
libraries for important stuff (hint, not javascript).

I am not sure how this advice is supposed to work though : the number of
problems that are solvable "in an afternoon" is very small.

Especially the problems I know will take only an afternoon to solve and won't
need to be maintained afterwards.

------
jkingsbery
In my 12 year professional career and 10 years of programming before that, I
can't count how many times I thought something was an afternoon of programming
and I was terribly terribly wrong.

Generally good advice to be conscious about taking dependencies, but it's also
worth sanity checking that something is really "an afternoon of programming."

~~~
TeMPOraL
Agreed. One has to learn to be realistic, and account for time spent thinking,
debugging and testing when coding from scratch. _left-pad_ is an afternoon
project. SQLite connector may be a week project. Full JSON parser? Let's book
a month to be safe.

------
browsergap
I like this, but not things I wrote instead of using existing lib took weeks,
not an afternoon. And I didn't think they would take an afternoon.

If it's trivial, probably use existing lib.

I don't know what my rule for deciding this is....I don't think I have one. I
just make decisions. If I don't like a library I won't use it.

------
vishnugupta
Obviously each situation is unique, however, I’ve been burned more often by
trying to clean up hand rolled out code than using a library.

I can recall quite a few instances where engineers, even experienced ones,
went down this path ended up trying to reinvent such age old technologies as
version control system, web server, just to name two.

------
KaiserPro
yeah nah, life is far too short for that.

Part of the reason why I use python is not because its "brilliant" its because
it has most of the stuff you need built in. I'm too old for supporting my own
tech debt.

Yes, its slow, yes there are bits that stink of poo. But 95% of the time there
is something that'll do the job, most of the time. I'm not going to put my
self on the hook for supporting the 0.5% cornercases the appear in 85% of all
incident reviews.

Should I need something specific, then of course I'll write it, but only after
google has said no, or the libs I've found don't work.

Its a silly rule, and I imagine stems from a young buck wanting to prove them
selves. Thats great, but I finish at 17:00, and I don't ever intend on staying
late. I suggest everyone tries it at least once. It'll make you a better
engineer, honest.

------
methodin
Time it takes to build something is not even remotely the best metric for
value or ease. Programming is never a box and things that took one afternoon
to write at first could be thousands of lines long a year from now. To me this
is as horrible as advice as it is to use dependencies for everything.

------
pldr1234
Never take afternoon advice from a programmer about how to run a business.

Because at the end of the day, that's what team management and engineering
teams are. Functional units of a business. And advice like the OP's will lead
you to death by a million shed bikes.

But for side projects, sure, have fun with it.

------
superjan
I agree with the author in that you should think twice about adding a
dependency. I’d be willing to invest way more than an afternoon, depending on
the circumstances. Adding a dependency is also work. How much depends on your
work environment, so you should think for yourself.

------
AtlasBarfed
So... why not just take the source of something you like and include it?

Probably if one "thinks" they can do it in a half an afternoon, you can look
at the source of something that simple for two or three things and just
clone/renamspace/whatever to the source.

------
luizfzs
It works great up to the point that you have one week to deliver a feature,
and that one afternoon is the difference between you delivering a half-tested
or well-tested feature.

Also, as pointed by other comments, people are generally not good at
estimating.

------
mberning
Mike Perham in 2016: [https://www.mikeperham.com/2016/02/09/kill-your-
dependencies...](https://www.mikeperham.com/2016/02/09/kill-your-
dependencies/)

------
SavageBeast
Never having thought about it before, I realize I usually go the opposite way.
By writing about a days worth of code on something - if only to realize its
true scope - then going in search of a lib of some kind to handle the dirty
bits.

------
Beldur
From the GO Proverbs:

,,A little copying is better than a little dependency."

[https://www.youtube.com/watch?v=PAAkCSZUG1c&t=9m28s](https://www.youtube.com/watch?v=PAAkCSZUG1c&t=9m28s)

------
randyrand
why not just copy the code from the dependency into your own sources?

~~~
btilly
You don't believe in software licensing, do you?

~~~
wrkronmiller
Vendoring dependencies is usually not the same thing as ripping off someone
else’s code.

It usually just boils down to keeping a copy of the version of a dependency
you used.

~~~
btilly
Copying someone else's code and putting it in your software repository is an
excellent way to find yourself violating a wide variety of open source
licenses.

This is actually one of the most common causes for GPL violations. And
companies have gotten into serious trouble for that.

Either rewrite the code, or maintain it as a dependency and follow the
licensing rules. Don't simply copy code from it into your project unless you
have explicit reason to believe that that is OK to do.

------
flavious
My rule scales with the scope of the dependency:

"If you think that creating a wrapper around the library is too expensive,
then the dependency is not worth having".

------
nijave
Shared dependencies can also reduce, code though. Do you really want 10
slightly different implementations of the same thing after you've brought in a
few large dependencies?

~~~
MrStonedOne
That almost never pans out because they all end up pinning different versions
of the same library.

~~~
wonderlg
“Almost never” is still more frequent than “never,” so it’s still positive.

------
cmckn
Many others have mentioned the problem with estimating time cost. I think the
core issue is that very few things actually take _only_ an afternoon of
programming to build.

One good example of this is a general application framework like Dropwizard or
Spring. These are relatively giant dependencies; and you could fairly easily
do without them early-on in a project (or maybe for the life of a project).
But, odds are, you'll spend an afternoon (or several) in the future
refactoring to use such a framework, because xyz feature would benefit from
that foundation. The judgement call of using it early or late is why we get
paid the big bucks!

------
hyperpallium2
Clarify: actually within an afternoon, not looks-like an afternoon (include
debugging, testing, docs, api design, etc see Brook's 3x). So, e.g.
leftShark().

------
wwright
Never spend an afternoon programming something that you may have to waste
future time debugging and supporting when a dependency already handles it
perfectly.

------
pjmlp
I rather take the afternoon for dealing with business related stuff,
documentation, unit tests, architecture diagrams, improving the CI/CD
pipeline.

------
Rantenki
This is not good advice for these reasons:

\- Hofstadter's Law: It always takes longer than you expect, even when you
take into account Hofstadter's Law. -> It's gonna take longer than an
afternoon. Always

\- Opportunity cost: Your app needs to DO something, and spending time
building dependencies steals quality from your core business proposition, even
if it's a small (Haha: see previous point) dependency.

\- While there are a ton of crap libraries out there, there are also a lot of
good ones, which include hard lessons learned, about even simple tasks. You
_could_ rewrite a very simple http client in an afternoon (telnet hostname
80\n GET / HTTP/1.0\n\n), but you'll probably have a ton of glaring flaws just
waiting to bite you _badly_.

------
aoeusnth1
I don’t have enough afternoons for this.

------
jdonaldson
Be right back, going to delete my html parsers deps and roll my own with some
regex. [https://stackoverflow.com/questions/1732348/regex-match-
open...](https://stackoverflow.com/questions/1732348/regex-match-open-tags-
except-xhtml-self-contained-tags/1732454#1732454)

------
Igelau
BRB, going to go roll my own AES -- what could possibly go wrong?

~~~
nullc
> BRB, going to go roll my own AES -- what could possibly go wrong?

Probably not much more than picking an AES library written from someone else.

It's fairly unlikely that your custom AES would function at all unless it was
functionally correct.

Your bespoke AES will likely have timing sidechannels, but so will most AES
you go pick off the shelf. (in fact, if you happen to need CBC mode, virtually
any AES you pick off the shelf will end up having timing side-channels,
because most code that doesn't is GCM only)-- particularly because it's hard
to be both high performance and side-channel free without using SIMD, and to
do that you need to operate on multiple blocks at a time.

In fact, since you happen to know your environment is targeting only hardware
with AES-NI (in my hypothetical), you'll just use that and you'll even be free
of side-channels too.

The "abstinence only cryptography" advice was originally about inventing your
own blockciphers and such, not implementing existing ones.

Okay so you're smart and so you'll want to go take your AES from some highly
reviewed sourse with lots of smart people. You pick.. say.. the Linux kernel.
Welp if you picked the plain C implementation in it, congrats, you just got
yourself a bunch of timing side-channels.

There are, of course, plenty of things that can go wrong in making your own
implementations-- so it's not entirely misplaced to apply it to
implementations-- but sadly you are not more likely to avoid them by simply
picking one written by someone else. If you understand the domain then you can
select a good library and evaluate it, but if understand that you could likely
also write your own. :(

On the plus side, a simple blockcipher isn't the sort of dependency that is
likely to have a substantial on-going cost either. It won't randomly change
its behaviour (we hope, at least not until haxors take over the upstream
repository), its functionality is simple and well specified, etc.

So it is probably fine to use one from a library and the OP's advice doesn't
apply. But all the reasons why the code you wrote would probably be broken?
They probably still apply to the library.

~~~
Igelau
Yes, yes, yes, and yes, but the crucial difference is that vulnerabilities in
the library are more likely to have the benefit of disclosure.

~~~
nullc
Eh. Yes, _more_ likely, though unless you're talking about something with
openssl level ubiquity it might not do much.

For example: my post pointed out that Linux's naive AES has a huge timing
sidechannel (e.g. the same bug JoeBob's would). This isn't news. It's also not
fixed.

Many times I've been asked to review a cryptographic library and found that it
had problems, had them for been for years.. Sometimes the issues had been
reported and just ignored.

In some cases reporting the issue just causes the author to take it down...
creating its own problems for people who were depending on it!

At the moment I have two private outstanding bug reports for total breaks in
cryptosystem library code that I just stumbled into while browsing the
internet where the authors/maintainers haven't replied and it's been more than
a month. After a bit longer, I'll make the reports in public, but I expect the
software will continue to go uncorrected (or just be taken down in response).

One piece of advice I'd give for anyone taking a dependency: go read through
its bug tracker of open bugs (and recently resolved ones) -- and their public
patch queue if they have one. Also do the same for all transitive
dependencies. You can gain some pretty valuable knowledge and more benefit
from shared bug finding.

Of course, if you're not a subject matter expert you might not be able to
judge if a report is correct or if the subject is serious-- though you will
probably be able to tell if the maintainers are active/responsive.

I gave a talk once on the problem of "Selection Cryptography"\-- where I argue
that merely _picking_ an implementation of cryptographic code (much less the
primitives to use) is an act of rolling your own cryptography that triggers
similar risks to writing some which also must be managed.

------
tobyhinloopen
My strategy: Use dependencies, but don’t depend on them.

------
pacomerh
nothing takes an afternoon of programming if it needs to be QA'd. At least
this is not good advice for a company. Possibly for personal projects.

~~~
earthboundkid
But you can add a dependency in less than an afternoon?

The exact same objection applies in both cases.

------
gauchojs
Afternoon = around 10 lines of code

------
jasonpeacock
This is horrible advice. There's a reason that you don't write your own
hashtable implementations.

Yes, I can write a hashtable implementation in an afternoon, but it's going to
have bugs that I'll spend the next year fixing, and still not achieve the
performance of the pre-built version.

All that work of finding existing solutions and learning how to use them?
That's part of the job.

Find a bug in the dependency? Submit a patch.

Worried about the dependency changing? Lock the version.

Too many external repos to retrieve those dependencies? Use a local cache.

Don't reinvent the wheel.

~~~
alkonaut
> I can write a hashtable implementation in an afternoon, but it's going to
> have bugs

If it has any bugs that would surface in a year of production (while the
dependency version wouldn't) then you didn't write an equivalent in an
afternoon.

The advice, if it's to be useful at all, must be things _that you could
completely replace in the same quality, in an afternoon_.

It's the left-pads and is-odds, to begin with.

~~~
avmich
> that you could completely replace in the same quality

And the quality of the original might be questionable for many cases.

------
blocked_again
That's like 90% of NPM.

------
wendyshu
I'd say the opposite

------
Azatik1000
Total bullshit

------
bellwether
This is a bad rule and even worse advice.

------
yoz-y
Not Invented Here Syndrome[1]

[1]:
[https://en.wikipedia.org/wiki/Not_invented_here](https://en.wikipedia.org/wiki/Not_invented_here)

------
gm
No. Some reasons:

1) As everyone else has said, we are horrible at estimating.

2) We think we might only need 1% of an external dependency, until we do not.
Case in point: By this criteria, no one needs a grid/datatable library.So we
write it "in an afternoon". Then we're asked for an export to Excel. Later on
we're asked for pagination with large data sets. And then an export to PDF.
How many afternoons did that cost you?

3) Writing new code is an excellent opportunity to create new bugs and
implement bad design because we're on hurry since it took nowhere near an
afternoon of code to write and we cut corners.

Anyway, those are just three that come to mind immediately.

This is not an argument against the "afternoon rule", but is really an
argument against making simple-sounding black and white rules to solve to
problems that are inherently complex, and are thus, gray areas.

~~~
ogre_codes
> We think we might only need 1% of an external dependency

I've seen the opposite just as often. Someone things an external dependency
can just plug in and solve the problem, then 16 hours later they've made the
basic thing work with an external dependency with a bunch of internal code on
top of it. Now you have a pile of fragile code which breaks when the
dependency changes underneath you, you have a bunch of in-house code
supporting a thing which might not be getting up-line support anymore, and you
still have a bunch of internal code to maintain.

> Case in point: By this criteria, no one needs a grid/datatable library.So we
> write it "in an afternoon". Then we're asked for an export to Excel. Later
> on we're asked for pagination with large data sets. And then an export to
> PDF. How many afternoons did that cost you?

You can always migrate to a library at a later date if the scope changes.
More-so, you don't _know_ how the scope is going to change, how do you pick a
dependency based on future scope changes?

~~~
gm
That's exactly what my point is: This is way too complex to boil down to a
simple rule of thumb to apply to all cases. There are arguments and counter
arguments (and real-life examples and counter examples) ad-nauseum.

It's better to say: "Hey experienced people: How would you decide whether to
introduce an external dependency or roll your own if the immediate requirement
at hand seems small enough to write on your own in half a day?". Then we can
get into a fun conversation that will boil down to "It depends."

~~~
ogre_codes
Developers should try to build things before they reach out and grab a
package. Usually it doesn't take too long to figure out if you are making a
giant hairball. By trying to implement it yourself, you get a better
understanding of the problem regardless.

