This is a blog version of the talk I gave at TorontoJS Lightning Talks event on Apr 30, 2024.
My technical pet peeve
This irritates me - typing a variable’s name beside the variable in the log statement. It isn’t that big of a problem for just a few variables here and there. But many variables multiplied by many files in your app? You do the math.
That's when I realized writing my own logging library could save me some headaches.
The rant continues...
Now, who enjoys commenting out or deleting log statements before pushing their code to production? I really don't.
Fortunately, there are tools available to solve this such as the debug
library which is based on the core NodeJS logging utility.
We already have a solution. So why bother? Let’s take a look at my personal use case:
More often than not, a variable is logged from within nested function calls, and across multiple files. When I wanted to quickly check from where a variable is being logged (that happens after you’re debugging and have called it a day and you return to it later), I would write the metadata (the function name) next to the log variable.
Instead of a quick check, this process became quickly tedious. Here was a use case that i hadn't found any JS library addressing (as far as I know).
Time to modify the wheel!
So, I decided to write a module that logs the value of the variable alongside its name without having to type the name manually. And I'll share with you how I did it from scratch.
Of course, this can be done using console.log()
with the object destructing { }
property.
But what console.log()
lacks is the ability to print the function scopes of the logged variable in a developer-friendly way. Here's the output:
By developer-friendly, I mean anyone seeing this log message in their terminal can immediately tell the function scope of the variable being logged. The order of function scopes is parsed from the call stack provided by the NodeJS stack trace API. Hence the name, scope-logger.
Note: “Object.” just means the outermost function is invoked from the global object/topmost level of the file.
Here's the recipe on how to write a library by studying others (in pseudo-code!):
Steps.length = 4
Step 0: Just build it!
“Zero” not only because we are programmers, also because it’s one fundamental step before taking any further. You must be building the Minimum Viable Product (MVP) as soon as possible, or at the very least, a small chunk of it.
You’re not striving for perfection. You just need a working version.
Step 1: Start small
As a reference, pick a small library with similar use cases to your idea.
Here’s my criteria for selecting one:
Popular: not because it’s the new kid on the block, but because it’s been well-maintained for a long while with a large, active user-base.
Well-tested: because quality is important - look for integration/e2e tests and unit tests.
Challenging enough for analysis: you don’t wanna be tackling the Express framework or React on your first go. Pick a library that is considerably smaller, which you can determine by the following factor.
Number of dependencies: one way to tell if a library is not too challenging to be used as study source is based on the production dependencies count. The fewer the better. For example, I chose
debug
because it only has 1 dependency (ms), while the rest of the code relies on core NodeJS modules - which is exactly what I was looking for - to learn how to build a library from scratch, not off the shelf libraries with many external deps, which in turn are based on more deps. There you go, dependency hell.
Step 2: Break it down smaller
After you’ve picked a library, break it down even smaller. Study how the main functions work by replicating their behaviors: isolate those functions, provide input, and see the output.
This is one of the few times that you should allow yourself to fall into rabbit holes, and really understand the in’s and out’s of each line of code in a function.
For example:
This is the formatArgs()
function of the debug
library. By isolating it, I learned about ANSI colour escape codes, and where and how the properties of one debug instance is used (namespace
, useColours
, and this.colour
)
Step 3: Publish
After you’ve studied other approaches and built your own module, all there's left to do is hit publish, right? Well, not before you complete these three things.
Semantic Versioning: for every update (major, minor, or patch) made, increment the version number according to semantic versioning.
Changelog/history: track and display the updates you’ve made so far - the version number, the date you published it, and a brief description of those changes.
- Documentation: You cannot expect other developers to magically figure out how to use your library without detailed descriptions of the features. It also serves as a roadmap of what you’ve built. You can either dedicate a whole website for it or simply fill in the README.md file.
Note: I left out tests because we should be writing them as the library is being built, not just before hitting publish.
Recipe Recap
- Zero: you build a working version.
- One: start small by picking a small but challenging enough library to learn from
- Two: analyze that library by breaking it down into even smaller pieces
- Three: after attaching semantic versioning, changelog, and documentation, hit publish.
Breaks are Great!
These steps are in a loop, repeated until it’s time for a break, which is just as important as the preceding steps.
As a rule of thumb, after publishing your major or minor version, or several patch updates in a row, take your well-deserved break!
Taking a break is great for discovering more use cases as you use the library/module you just made in other projects.
As you might’ve guessed, I did use scope-logger
to develop other modules. By doing so, I found features that I really needed, not just stuff that I thought would be cool to have.
When a particular use case keeps popping up, that’s my cue to update the library with a new feature to address it.
Key Takeaways:
Solve your own problems first: As a budding developer, how do you go about determining what projects to pursue? The idea for this library occurred to me as I was building projects for my portfolio. With limited industry connections and programming know-how to cater to a target audience, I felt stuck. But there lurked a hidden opportunity. If you're in the same boat, try this: ditch the latest trends, and double down on understanding the nuts and bolts of one technology, to tackle a problem that's currently bogging you down.
Reinvent the wheel, sometimes: And to solve your developer problem, contrary to conventional advice, you must reinvent the wheel to understand in-depth how the wheel is built. In the long haul, the time and effort put into studying a great library, and building one using core modules and fundamental programming principles, is what’s gonna make you stand out.
Depth && Breadth (not versus): With that said, depth is not the superior path. Tread both. The breadth part involves exploring other libraries, studying their differences, and observing patterns that show up repeatedly.
So, I invite you to try this project idea - create a library by studying great examples. As slow as it may seem, your programming craft will grow exponentially.
Top comments (0)