Find a comfortable position. Close your laptop halfway, so the screen light softens but the fan noise continues.1 That hum is your anchor. You will return to it throughout this practice.
Take a deep breath in. Hold it. Now run npm install. Breathe out slowly as 1,247 packages are added. Do not look at the output. You are not ready.
Body scan
We will begin with a body scan. Bring your attention to the top of your head. Notice any tension you are holding there. This is where you store your awareness of packages that have not been updated in three years but still work. Let it soften. Those packages do not need you right now. They are dormant, the way a perennial is dormant. The roots are still in the ground. There is nothing for you to do until spring, and spring may never come, and that is also part of the garden.
Move your attention to your forehead. This is where you hold the memory of every major version bump that removed the one function you actually used. The migration guide was six pages long and the first step was “update your mental model.” Breathe into that space. Release it. You have already updated your mental model. You updated it when you pinned to the previous version and moved on.
Now bring awareness to your jaw. You are clenching it. You have been clenching it since the last time you saw ERESOLVE unable to resolve dependency tree. Unclench. The tree will not resolve faster because your jaw is tight. The tree may not resolve at all. That is also acceptable. We are practising acceptance today.
Feel your shoulders. They have been raised since you read the GitHub issue titled “Is this project still maintained?” posted fourteen months ago with no response. Lower them. The maintainer may be on a goat farm in Portugal. The maintainer may be the goat. You cannot control this. Release your shoulders and release the maintainer.
Your hands are hovering over the keyboard. Notice which keys your fingers are drawn to. If they are drawn to the up arrow and enter, you are about to re-run a command that has already failed. This is not mindfulness. This is while true. Gently place your hands in your lap.
Breathing exercise
We will now practise a breathing technique calibrated to the dependency lifecycle.
Breathe in for four seconds. This represents the planting season, when the package is new and everything works and the README has a row of green badges and the soil is warm.
Hold for seven seconds. This represents the long growing season, when issues accumulate faster than they are closed and the CI badge turns grey because the service it was hosted on has itself been abandoned. The plant is alive but nobody is watering it. Weeds are moving in. You can see them in the issue tracker.
Breathe out for eight seconds. This represents the harvest and rot, when a blog post titled “Why We Moved Away From X” appears on Hacker News and the comment thread is 400 messages long and the top comment is “I said this would happen two years ago” with no link to the original prediction.
Repeat this cycle. In for four. Hold for seven. Out for eight. If you lose count, that is fine. Counting is implemented differently across locales and your breathing may be lexicographically sorted.
Visualisation
Close your eyes. You are standing in a garden. This is your project. You planted some of what grows here, chose it carefully, read the labels, checked the light requirements. But most of what you see was not planted by you. It arrived as seeds carried inside other plants, hidden in the root ball of a package you selected for its flowers without thinking about what it would bring with it. This is transitive dependency. Every garden has it. You are not a bad gardener. You are a gardener.
Walk deeper into the garden, past the border plants you recognise and tend. Past the mid-layer shrubs whose names you have seen in your lockfile but never looked up. Keep walking until you reach the back fence, where the growth is dense and tangled and the packages are maintained by a single person in a different timezone whose GitHub profile picture is a sunset from 2016.
Look down. The soil here smells faintly of sulfur. This is normal. The sulfur is coming from a transitive dependency six levels deep that pulls in a native binding to a C library that was last updated during the Obama administration. The binding works. Nobody knows why. Its roots have grown into your foundation in ways that would be unwise to investigate. Sit with this. Breathe. Every garden has something growing behind the shed that you have decided not to identify.
Now, from this place of stillness, I want you to visualise a yellow advisory banner. It says 3 moderate severity vulnerabilities. Do not open your eyes. Do not run npm audit fix. The fix will update a package that will break a peer dependency that will cascade through your lockfile like pulling a weed whose roots have wrapped around everything else in the bed. You know this because you have done it before. Instead, observe the advisory. Let it be. Moderate severity means someone could theoretically exploit a ReDoS in a markdown parser if they controlled the input and had four hours and very specific motivations. This is not your concern today.
Mantra
Repeat after me, silently, in the voice of your internal monologue:
I accept the packages I cannot update.
I accept the breaking changes I cannot predict.
I accept that node_modules is larger than my application and always will be. The undergrowth is always larger than the tree.
I accept that the package I depend on depends on a package that depends on a package whose maintainer closed all issues with the message “I am mass-closing issues. If your issue is still relevant, please re-open it,” and then transferred the repository to an organisation with no other public repositories.
Breathe.
I accept that --legacy-peer-deps is not a solution. It is a coping mechanism. I am at peace with my coping mechanisms.
I accept that somewhere in my lockfile there are two versions of the same package and they are both wrong. Two plants with the same name that fruit differently. I will not dig them up today.
Letting go
You carry your dependency tree with you when you leave your desk. You carry it into the shower, where you think about whether that package you added last week is still maintained. You carry it to bed, where you wonder if the lockfile you committed on Friday will still resolve on Monday. You carry it to dinner with friends, where you do not mention it, but it is there, a background process consuming resources, never quite idle.
Notice this. You are anxious about code that is not running. You are worried about packages that are sitting in a registry, unchanged, doing nothing. The vulnerability you read about on Tuesday has not been exploited. The deprecation warning you saw has no deadline. The package whose maintainer went quiet is still working, today, right now, exactly as it was when you chose it. Nothing has happened. You are holding tension for things that have not happened yet and may never happen.
Put the garden down. You are not in the garden right now. The garden does not need you at midnight. The weeds will not grow faster because you are thinking about them. The frost will not come sooner because you are watching the forecast. Your dependency tree is a text file on a server and it will be the same text file tomorrow morning.
Breathe in. You are not on call for your lockfile.
Breathe out. The packages can wait. They have always been waiting. That is all they do.
Guided acceptance: the six dependency griefs
Version conflict. Two packages in your tree require different major versions of a third package. You cannot have both. You cannot have neither. You can have one if you alias the other, which means your application will ship two copies and they will not share state and something will break at runtime in a way that no type checker or linter will catch. Observe this situation. Do not try to fix it. It was not introduced by a person. It was introduced by time. Time moved the versions apart the way two branches of the same tree grow in different directions, slowly and without malice, until they are reaching for different light entirely and you are the trunk expected to feed them both.
Abandonment. A package you depend on has stopped growing. The last commit was a Dependabot PR that was never merged. The README still says “PRs welcome!” in a way that now reads as historical document rather than invitation. You could fork it. You could tend the fork. You could become the person whose GitHub profile picture is a sunset, who mass-closes issues, who gets emails from strangers asking why the leaves are brown. Sit with this. The package gave you what it could for as long as it could. Send it gratitude. Let it decompose. The nutrients will feed the next thing that grows in that spot.
The phantom dependency. Your application works in development but fails in CI because you are relying on a package that you never declared as a dependency. It is installed because another package depends on it, and your package manager hoists it to a location where your code can reach it, and you imported it eighteen months ago without realising it wasn’t yours to import. You have been eating fruit from your neighbour’s tree, the branch that overhangs your side of the fence, and now your neighbour has cut the branch. The phantom dependency is a lesson in impermanence. Acknowledge it. Plant your own. Or don’t. Either way you are choosing a form of suffering, and choosing is itself a practice.
The security advisory that does not apply. You have received a security advisory for a vulnerability in a package you use. The vulnerability is in a function you do not call, in a code path you do not exercise, in a scenario that requires an attacker to control a YAML parser’s input while running as root on a machine that accepts unsigned certificates over an unauthenticated HTTP endpoint. The advisory is marked Critical. Your security dashboard is red. Your compliance team has opened a ticket. The fix requires upgrading to a version that drops support for the Node.js version you are running in production. Breathe in. This is suffering caused by metrics. A garden inspector has told you that one of your plants is on a list of invasive species in a country you do not live in. The metric says you are insecure. You are not insecure. You are measured.
The merge conflict in the lockfile. Two branches have grown toward the same light and met in the middle. You and a colleague both updated dependencies, and now the lockfile has 4,000 lines of conflict markers. You will not read them. Nobody reads them. The lockfile is not meant to be read by humans, which is why git insists on showing it to you in a format designed for humans to resolve. This is a tangled vine that cannot be unknotted, only severed and replanted. Run git checkout HEAD -- package-lock.json and then npm install and let the tree grow back from your side. Your colleague’s changes will need to be re-resolved. This feels wasteful. It is wasteful. Gardening is mostly waste. Seeds that don’t germinate, cuttings that don’t take, entire seasons of growth pulled up because something else needed the space. Release the guilt. Delete the conflict. Begin again.
The everything update. It has been eight months since you last tended your dependencies. You have been meaning to. The longer you wait the harder it gets, and the harder it gets the longer you wait, and this recursion has no base case. Today you run npm outdated and the output is longer than your application. Thirty-seven packages have new major versions. The changelogs reference other changelogs. The migration guides assume you completed the previous migration guide. You are four migrations behind. This is the garden you left over winter. The gate is stuck. The paths are overgrown. Something has built a nest in the trellis. Observe the fear. Name it. It is called “I am going to spend three days on this and my application will behave exactly the same when I am done.” That is correct. It will behave exactly the same. That is the practice. Maintenance is a meditation on impermanence disguised as wasted effort. The garden does not look different after you weed it. But you were there. Your hands were in the soil. That is enough.
Closing
Bring your awareness back to your breath. Back to the hum of your laptop fan. Notice that it is louder now. Something in your node_modules has triggered a post-install script that is compiling a native module. Or mining something. You cannot know which from the sound alone, and knowing would not change the sound. Listen to the gentle sound of endless blocks being chained together. Let it run. You agreed to this when you ran npm install. You agree to it every time. Whatever is happening in that process is happening now, and it will finish when it finishes, and what will be will be.
When you are ready, open your eyes. Open your laptop fully. Look at your terminal. If there are warnings, let them scroll past. They have always been scrolling past. You have always been here, in this garden, between npm install and the next breaking change, tending what you can and accepting what you cannot.
Go gently. The growing season is long and the soil does not care about your deadlines.
This meditation was tested on Node.js 18, 20, and 22. Results may vary on earlier versions due to a breaking change in the --harmony-weakrefs flag that affects garbage collection of abandoned inner peace. If you experience dizziness, ensure your lockfile is committed and try again after clearing your module cache.
-
If you have a fanless Mac, this website will provide a suitable imitation. Match the volume to whatever feels grounding. ↩