Great Developers Steal
Published Mar 7, 2021
Table of Contents
- Good Developers Copy, Great Developer Steal
- Third-Party Code
- Open Source Education
- Vendor
- Conclusion
Good Developers Copy, Great Developer Steal
We often reach for a library to solve a problem for us. You can write everything from scratch, and thereās pride and feeling of ownership in doing so, but itās not realistic time-wise. We should strive to at least understand what weāre including in our code. Often we donāt need the entire library, but just a piece of functionality.
The title is a play on words from the famous Pablo Picasso quote: āGood artists copy, great artist stealā. Itās not talking about stealing as in theft, but taking ideas, and inspiration from others making them your own. Without understanding we start to rely on our tools to do the thinking for us. The best cure to impostor syndrome is experience which is gained by doing. Donāt follow a path set by others. Itās going to ring hollow.
Third-Party Code
Third-party code is anything you yeet from StackOverflow, or a library you include. Itās code you didnāt write yourself. Whatās the difference between copying a random piece of code from StackOverflow, and including a package from npm? If we think about it, not a lot. In the case of the package, we often donāt even look at the source code. We blindly trust the code not to format our system, and work as advertised.
Thereās a lot of packages on npm ā over a million. Thatās a lot. Take a package like is-odd with over half a million monthly downloads that does what the name implies ā checks if a number is odd. It doesnāt take a genius to realize we probably donāt need a dependency for a single line of code thatās (n % 2) === 1
. It even includes another dependency is-number with ~45 million weekly downloads! Let me make one thing clear. Iām not trying to shame, or make fun of anyone. Iām just trying to point out how many of us reach for these things. Your favorite libraries, and frameworks include some of these as dependencies.
What Iām trying to say is that before we include a library, we should consider if we need the entire library.
In a recent project I had to convert a number to a fraction, and vice versa. I only had one problem ā Iām not great at math. Turning a fraction to a decimal was simple enough, but doing the opposite not so. I was looking over at StackOverflow threads, and being reminded of my years in school while I was resting my eyes in math class.
First I found mathjs and thought āsweetā to myself. It can easily do everything I want. I used BundlePhobia to check the size, and to my surprise it was almost 160kb ā if that doesnāt mean anything to you, thatās larger than the framework (React) Iām using. š±
Of course, you wouldnāt ship the entire thing to the browser. It hopefully uses tree shaking which should eliminate code thatās not being used leading to a smaller bundle size.
Open Source Education
The fastest way to learn is looking through other peopleās source code. Donāt wait for someone to show you how to do something. You can learn yourself by looking at any public repository on GitHub. This is what I mean by open source education.
I found number2fraction. I decided to look into how it works. I could have done the same with mathjs, but it was using fraction.js as another dependency. The package is tiny in comparison, but the same holds true.
We can search for the name of the method used in the API to find where itās located. In this case itās number2fraction()
. If you canāt find what youāre looking for, clone the repo to your machine. You can also try importing it to CodeSandbox. This is why Iām excited about GitHub Codespaces.
Anyhow, the search lead me to a couple of results. I could quickly identify the one Iām looking for being number-to-fraction.ts.
Files including .spec
(short for specification) are also helpful. Theyāre test files to ensure the code works as intended. Itās useful to us, because we can see how itās used if we have to (the reason being incomplete, or hard to understand documentation).
Donāt be intimidated at looking at TypeScript files. TypeScript is JavaScript. You can ignore type annotations. You can always copy the piece of code, and remove TypeScript to not distract you. In a lot of situations, the code should also have some helpful comments.
const decimalPattern = /^\d+\.\d+$/
/**
* convert decimal number to fraction
*/
export function number2fraction(n: number): string {
if (!decimalPattern.test(n.toString())) {
return n.toString()
}
const strNum = n.toString()
const afterDecimal = strNum.length - 2
const denominator = Math.pow(10, afterDecimal)
const numerator = n * denominator
const divisor = gcd(numerator, denominator)
return `${numerator / divisor}/${denominator / divisor}`
}
/**
* find greatest common divider between x & y
*/
function gcd(x: number, y: number): number {
if (!y) {
return x
}
return gcd(y, x % y)
}
I prefer cleaning things up, including variable names. It makes understanding whatās going on a lot simpler. Thereās barely any TypeScript, but Iām going to remove it for demonstration purposes.
Thereās not much to it. After cleaning it up I go over each line. If you donāt understand how something works, you donāt need the person who wrote the code to explain it to you. Make a note of it, and open Discord to get help. Make sure you frame the question, so itās coherent.
/*
regex pattern that checks if the number is a decimal,
paste it into https://regexr.com/ to learn how it works.
*/
const isDecimal = /^\d+\.\d+$/
export function number2fraction(number) {
/*
checks if passed in value is a decimal,
you can learn more about https://mdn.io/test.
*/
if (!isDecimal.test(number.toString())) {
/*
if not, return string representation because the
function expects the return to be a string.
*/
return number.toString()
}
// store stringified number, for example '1.5'
const stringifiedNumber = number.toString()
/*
return amount of numbers after the decimal point where the
subtracted amount represents the number, and period so
it works for '1.5', but would break for '10.5'.
not sure why they didn't use a more reliable method such as
'10.5'.split('.')[1].length.
*/
const afterDecimal = stringifiedNumber.length - 2
/*
the denominator is the number below the fractional bar.
this is just returning the base based on after decimal amount,
for example '1.5' equals 10, or '1.50' equals 100.
*/
const denominator = Math.pow(10, afterDecimal)
/*
the numerator is the number above the fractional bar,
for example 1.5 * 10 equals 15.
*/
const numerator = number * denominator
/*
if we read about
https://en.wikipedia.org/wiki/Greatest_common_divisor
we can see it can be used to reduce a fraction.
*/
const divisor = greatestCommonDivisor(numerator, denominator)
/*
we had a fraction of 15/10 and calculcated the divisor to be 5.
now we only have to return the fraction 15/5 equals 3,
10/5 equals 2, meaning 1.5 is equal to 3/2.
if we want to simplify this further to 1 1/2 we can learn how by
using https://www.calculatorsoup.com/ which is a great way to
learn how it works step by step.
*/
return `${numerator / divisor}/${denominator / divisor}`
}
/*
this is a recursive function that invokes itself until
there's no remainder left after doing x % y.
we can learn how it works by writing the steps on paper
instead of doing mental gymnastics.
*/
function greatestCommonDivisor(x, y) {
if (!y) {
// y is falsy, so return x
return x
}
/*
x = 15, y = 10
x = 10, y = 5 (15 % 10)
x = 5, y = 0 (10 % 5)
*/
return greatestCommonDivisor(y, x % y)
}
Vendor
Sometimes a package you include works, but lacks a feature youād want. You can contribute to the project, but āthe deadline was yesterdayā. There could be a lot of other reasons why youād want to do this. Maybe you have security concerns. You donāt want to be a victim of cryptojacking. Either way, in your project structure you can create a vendor
folder to store your third-party code. Package managers are just glorified file downloaders, and dependency resolvers. The only downside is that youāre also the maintainer.
Conclusion
Looking at things through the lens of code can crystalize things for us. We donāt need to have a deep understanding of everything. If we gain enough knowledge, and insight to gain confidence that things arenāt as scary as we believe itās only a benefit to us.
Donāt use not being good at something as an excuse. Given enough time, you can learn how most things work. Learning how to learn is analogous to āGive a man a fish, and you feed him for a day. Teach a man To fish, and you feed him for a lifetimeā. Questions are your fishing rod. Enjoy fishing. š£