Shelpers: A Better Approach to Development Shell Scripts in Nix
If you’ve spent any time managing development environments with Nix, you’ve probably accumulated a scripts/ folder full of bash scripts. And if you’ve done that, you’ve probably experienced the pain points that come with it: directory-dependent execution, cache invalidation confusion, and the perpetual question of “wait, do I need to re-enter my shell for this to work?”
Shelpers is a library that addresses these problems systematically. But before diving into what Shelpers does, it’s worth understanding why these seemingly minor friction points matter—both for individual developers and for the organizations employing them.
The Hidden Cost of Development Environment Friction
Research from Gloria Mark at UC Irvine shows that knowledge workers are interrupted every 6-12 minutes on average, and it takes approximately 23 minutes to fully regain focus after each interruption. For developers, this problem is particularly acute. Unlike many knowledge workers, developers maintain complex mental models of systems—data flows, dependencies, edge cases, and architectural decisions—that must be reconstructed after every context switch.
A study by Parnin and DeLine found that developers who face frequent interruptions show signs of mental fatigue earlier in the day, leading to more errors in afternoon work. The Carnegie Mellon Software Engineering Institute has documented that developers juggling multiple projects spend just 20% of their cognitive energy on actual productive work.
This matters for Shelpers because development environment friction—scripts that fail unexpectedly, commands that require specific directory contexts, or tools that behave inconsistently—represents a category of self-inflicted context switches. Every time a developer has to stop and think “why didn’t that work?” or “do I need to re-enter my shell?”, they’ve been forced out of their productive flow state.
The financial implications are significant. According to analysis from StackOverflow, the average developer’s time costs approximately $83 per hour. If a developer loses even 30 minutes daily to environment friction—a conservative estimate—that’s nearly $10,000 per developer annually in lost productivity. Scale that across a team of 20, and you’re looking at $200,000 in friction costs that don’t show up on any budget line item.
What Shelpers Actually Does
At its core, Shelpers is a library for building shell scripts that integrate cleanly with Nix development environments. Rather than sourcing raw bash scripts or defining functions directly in your shell hook, you declare your scripts through a simple Nix configuration, and Shelpers handles the rest.
The result is scripts that behave predictably, document themselves automatically, and integrate seamlessly with both your devshell and the Nix flake app system.
Shelpers was developed at Platonic.Systems to address recurring friction points that emerged across multiple client projects. The library is available on GitLab and represents a deliberately minimalist approach—solving specific, common problems without introducing unnecessary abstraction.
The Technical Problems Worth Solving
Directory Independence
Every developer has experienced this: you’re deep in some subdirectory of your project, you run a script, and it fails because it expected to be run from the project root. The workaround is always some variation of cd $(git rev-parse --show-toplevel) && ./scripts/whatever.sh && cd -.
This pattern introduces cognitive overhead and creates a category of “it works on my machine” bugs where scripts succeed or fail based on the developer’s current working directory.
Shelpers eliminates this entirely. Scripts execute identically regardless of your current working directory within the project. The mechanism is transparent—Shelpers handles the path resolution automatically, so developers never need to think about it.
Lazy Loading Without IFD
Here’s a subtle but significant problem with typical Nix devshell setups: everything your shell might need must be built before you can enter the shell. If you have a script that depends on some heavyweight derivation you rarely use, you still pay that build cost every time you enter your development environment.
Worse, if you want a script that depends on your own project’s build output and your project doesn’t currently compile, you can’t enter the shell at all. You’re locked out of your development environment precisely when you need it most.
Shelpers implements lazy loading without any import-from-derivation (IFD) tricks—a significant technical achievement that keeps your Nix evaluation pure and predictable. Scripts are built on first invocation, not on shell entry.
The mechanism is straightforward: the functions injected into your shell are thin wrappers that invoke a flake attribute, deferring the actual build until runtime. This means you can always enter your devshell, even if some of your scripts’ dependencies are temporarily broken.
Caching and Evergreen Behavior
By default, Shelpers caches script builds after first execution for instant subsequent runs. But you can disable caching per-script, which gives you evergreen behavior: the script rebuilds on every invocation, always reflecting your latest changes.
This matters when your script depends on your project’s build output. With evergreen mode, you don’t need to re-enter your shell to pick up changes. The script just uses the current state of your code.
Traditional Nix devshell approaches require exiting and re-entering your shell to pick up changes—a pattern that disrupts flow state and adds friction to tight development loops. Research on developer productivity from Microsoft found that developers with uninterrupted focus completed tasks faster and felt more satisfied doing so. The evergreen option directly supports maintaining that focus.
Automatic App Integration
Some scripts make sense to run outside the devshell. Linting, formatting, simple utilities—you might want to invoke these via nix run without waiting for the full development environment to initialize.
Shelpers makes this trivial. Set make-app = true in your script configuration, and it’s automatically exposed as a flake app. No additional wiring required.
This distinction matters for CI/CD pipelines, quick checks before commits, and any scenario where developers want to run a single command without the overhead of entering a full development environment.
The set -e Problem
This one is subtle enough that many developers don’t realize it’s a problem until they hit it.
When you define a bash function (as opposed to a script), that function executes in your current shell, not a subshell. If you use set -e to exit on errors—a reasonable practice—and an error occurs, you don’t just exit the function. You exit the entire shell. Your devshell closes and you’re back at your system prompt, confused about what just happened.
Shelpers provides set -e semantics without set -e. It traps errors and returns the appropriate exit status while keeping your shell alive. This is implemented automatically for all Shelpers functions.
Additionally, any set options you use within a Shelpers function are localized. They’re restored to their previous values after the function completes, preventing your debugging session from polluting your environment.
Environment Modification
Unlike scripts (which execute in subshells), Shelpers functions can modify your environment. This enables patterns like:
- Database setup scripts that export connection strings
- Development mode toggles that set feature flags
- Configuration loaders that source environment files
The transcript mentions a common use case: using a tool that builds a local Postgres installation in your project directory, with Shelpers wrappers providing db_reset, db_create, db_destroy commands. These commands need to share state via environment variables, which is only possible with functions rather than scripts.
These capabilities come with a tradeoff: functions that modify the environment can’t be exposed as flake apps, since apps run in isolation. Shelpers handles this gracefully—it simply won’t expose environment-modifying scripts as apps.
Self-Documenting by Default
One of the more opinionated design choices in Shelpers: documentation is not optional. Every script requires a description. Setting the description to an empty string is an explicit act.
This design choice reflects research on developer onboarding and knowledge management. According to Stack Overflow’s 2024 Developer Survey, companies with structured onboarding see 62% faster time-to-productivity. A significant component of effective onboarding is reducing reliance on tribal knowledge—the unwritten institutional knowledge that exists only in experienced team members’ heads.
Brandon Hall Group research shows organizations with robust onboarding processes improve new hire retention by 82% and productivity by over 70%. Self-documenting development environments directly support this goal.
When you enter a devshell that uses Shelpers, you get an immediate overview of available commands. These are color-coded to indicate:
- Whether the script can be run as a flake app
- Whether it’s cached or evergreen
This eliminates the archaeology session that typically accompanies returning to a project after a few months away. The available tooling is surfaced automatically.
For teams, this has measurable impact. GitLab research notes that slow onboarding leads to reduced productivity, higher turnover rates, and potential security risks. Developers who struggle to get up to speed may become disengaged, increasing the likelihood of attrition. According to IDC, the average cost of losing a developer within the first year is $115,000—self-documenting tools help prevent that loss.
The Business Case for Developer Experience Investment
Developer experience investments historically struggle to get executive buy-in because the benefits seem intangible. But research from DX (formerly Devex) provides concrete metrics: organizations with top-quartile Developer Experience Index scores show 4-5 times higher engineering speed and quality than bottom-quartile performers. Each point gained in their Developer Experience Index saves 13 minutes per developer weekly.
The ROI of developer experience improvements ranges from 151% to 433%, according to industry research. Companies investing in developer experience saw 77% faster time to market and 75% improved customer attraction and retention.
Shelpers represents a small, targeted investment in this category. The setup cost is minimal, and the ongoing maintenance cost is effectively zero once integrated. The return comes in reduced friction during daily development work, faster onboarding for new team members, and fewer “it works on my machine” debugging sessions.
More specifically, Shelpers addresses what the Carnegie Mellon Software Engineering Institute calls “build and integration debt”—a category of technical debt that lives in development infrastructure rather than production code. Their research in “Managing Technical Debt: Reducing Friction in Software Development” identifies this category as particularly insidious because it affects every developer on every working day but rarely appears in sprint planning or technical debt discussions.
Comparison with Mission Control
Mission Control, part of the flake-parts ecosystem, solves similar problems. The project is also from Platonic.Systems, and the README now recommends migrating to just-flake for new projects. However, understanding the differences illuminates Shelpers’ design philosophy.
Dependency: Mission Control requires flake-parts. Shelpers is standalone and integrates with any flake structure. For teams not already using flake-parts, this represents significant additional complexity.
Opacity: Mission Control leans heavily on the Nix module system’s implicit behavior. The flake file for a Mission Control project often doesn’t mention Mission Control anywhere explicitly—everything is wrapped in imports that trigger behavior through the module system. This is powerful but can be difficult to trace when debugging.
Shelpers is deliberately transparent—you can see exactly what’s being generated and where it goes. When something goes wrong, the debugging path is clear.
Philosophy: Mission Control provides a DSL for script management. Shelpers gives you raw script access with minimal automation on top. If you want to understand what your development environment is actually doing, Shelpers makes that straightforward.
Comparison with devenv
devenv is another popular tool in this space, focused on making Nix development environments more accessible. It offers faster evaluation through caching, automatic direnv integration, and high-level language modules.
devenv and Shelpers address different layers of the problem:
- devenv optimizes environment creation, package management, and process supervision
- Shelpers optimizes script management within whatever environment you’re using
They’re complementary rather than competitive. A project could use devenv for environment management and Shelpers for script organization. The key distinction is that Shelpers focuses narrowly on solving script-related friction points, while devenv provides a broader (and more opinionated) development environment framework.
When to Use Shelpers
Shelpers adds value to any project with repeated shell operations. The setup cost is minimal, and adding new scripts after initial integration is trivial—just a name, description, and the script body.
The payoff increases with project complexity: more scripts, more team members who need to understand the available tooling, more situations where lazy loading and evergreen behavior prevent confusion.
Specific indicators that Shelpers would help:
- Multiple developers regularly ask “how do I run X?”
- New team members take more than a day to set up their development environment
- Scripts fail unexpectedly based on working directory
- Developers forget to re-enter their shell after making changes
- CI runs commands differently than local development
For simple personal configurations or projects with no shell scripts, the overhead isn’t justified. But for anything involving regular script invocation during development, Shelpers provides a structured solution to problems that otherwise accumulate as technical debt.
Getting Started
The Shelpers repository includes a complete example in its README. The integration pattern involves:
- Adding Shelpers as a flake input
- Calling
eval-shelperswith your script configurations - Wiring the outputs into your shell hook, apps, and the special
shelpersflake attribute that enables lazy loading
A minimal configuration looks like:
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
shelpers.url = "gitlab:platonic/shelpers";
};
outputs = inputs:
let
system = "x86_64-linux";
pkgs = inputs.nixpkgs.legacyPackages.${system};
shelpers = (inputs.shelpers.lib pkgs).eval-shelpers [
({ shelp, ... }: {
shelpers."."."General Functions" = {
inherit shelp;
hello-world = {
description = ''echos "Hello, World!"'';
script = ''echo "Hello, World!"'';
};
};
})
];
in
{
devShells.${system}.default = pkgs.mkShell {
shellHook = ''
${shelpers.functions}
shelp
'';
};
apps.${system} = shelpers.apps;
shelpers.${system} = shelpers.files;
};
}Once the boilerplate is in place, each new script is just a few lines of configuration.
The Compound Effect of Reduced Friction
Force multipliers in engineering are investments that pay returns over time. A one-time setup cost of a few hours for indefinite time savings is the equivalent of investing in a dividend-paying asset.
Shelpers represents exactly this pattern. The initial integration takes an hour or two. After that, every developer on the project saves small amounts of time daily—time that would otherwise go to directory navigation, shell re-entry, documentation lookup, and environment debugging.
Those savings compound. Research shows that technical debt—including development environment debt—wastes 23-42% of developers’ time. Addressing even a fraction of that debt yields significant returns.
More importantly, reducing friction reduces frustration. Developers experiencing flow state—what psychologist Mihaly Csikszentmihalyi identified as peak performance condition—produce higher-quality work and report greater job satisfaction. Tools that protect flow state by eliminating unnecessary interruptions contribute to both productivity and retention.
Shelpers won’t make for exciting demos. It’s unglamorous infrastructure work. But it will make your development environment more predictable, more discoverable, and less prone to the small frustrations that accumulate across a project’s lifetime.
For teams using Nix who haven’t yet addressed the script management problem, Shelpers offers a proven, battle-tested solution. The investment is small, the risk is minimal, and the return is immediate and ongoing.
Shelpers is available at gitlab.com/platonic/shelpers. The library is actively maintained and has been deployed across multiple production projects at Platonic.Systems.