Writes idiomatic, performant, and maintainable Nix code. Covers best practices, anti-patterns to avoid (like `with`), module system design, and performance optimization.
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
skills listSkill Instructions
name: writing-nix
description: Writes idiomatic, performant, and maintainable Nix code. Covers best practices, anti-patterns to avoid (like with), module system design, and performance optimization.
Writing Nix
Core Principles
- Declarative over Imperative: Describe what, not how.
- Explicit over Implicit: Avoid magic scoping or hidden dependencies.
- Hermetic: No side effects, no network access during build (except fixed-output derivations).
Critical Anti-Patterns
1. The with Statement
NEVER use with. It breaks static analysis, tools (LSP), and readability.
# BAD
meta = with lib; { license = licenses.mit; };
# GOOD
meta = { license = lib.licenses.mit; };
2. Recursive Attributes (rec)
Avoid rec when let-in suffices. rec can cause infinite recursion and
expensive evaluation.
# BAD
rec {
version = "1.0";
name = "pkg-${version}";
}
# GOOD
let
version = "1.0";
in {
inherit version;
name = "pkg-${version}";
}
Module System
Standard Pattern
All modules must follow this structure:
{ config, lib, pkgs, ... }:
let
cfg = config.khanelinix.path.to.module;
inherit (lib) mkIf mkEnableOption mkOption types;
in {
options.khanelinix.path.to.module = {
enable = mkEnableOption "module description";
# Other options...
};
config = mkIf cfg.enable {
# Configuration implementation
};
}
Option Design
- Namespacing: Always prefix with
khanelinix.. - Types: Use strict types (
types.bool,types.str,types.listOf types.package). - Defaults: Use
mkDefaultfor overridable values.
Performance Optimization
Evaluation
- Laziness: Nix is lazy. Don't force evaluation of unused attributes.
- Imports: Avoid importing large files if only a small part is needed.
- Regex: Avoid expensive regex operations in hot loops.
Build
- Closure Size: Split outputs (
dev,doc,lib) to reduce runtime dependencies. - Filters: Use
lib.cleanSourceornix-filterto avoid rebuilding on irrelevant file changes (e.g., README updates).
Idiomatic Functions
Destructuring
Always destructure arguments in function headers.
# BAD
args: stdenv.mkDerivation { name = args.pname; ... }
# GOOD
{ stdenv, pname, version, ... }: stdenv.mkDerivation {
inherit pname version;
...
}
Overrides
override: Change function arguments (inputs).overrideAttrs: Change derivation attributes (steps, build inputs).
Formatting
- Use
nixfmt(ortreefmt). camelCasefor variables.kebab-casefor attributes/files.
See Also
- Validation: See validating-nix for checking syntax, formatting, and building code
- Module scaffolding: See ../../khanelinix/scaffolding-modules/ for creating new modules with proper structure
More by khaneliman
View allCreate development environment templates and project scaffolding with Nix flakes. Use when creating new project templates, setting up dev shells, configuring language-specific environments, or integrating with CI/CD.
Manages encrypted secrets using sops-nix and age. Use when adding new secrets, rotating keys, debugging secret access, or setting up secret management for new hosts/users.
Conduct thorough, actionable code reviews focusing on critical issues. Use when reviewing pull requests, analyzing code changes, identifying bugs, security vulnerabilities, or suggesting improvements that developers will actually implement.
khanelinix directory structure and module placement. Use when creating new modules, deciding where files belong, or understanding the modules/ organization. Covers platform separation (nixos/darwin/home/common) and auto-discovery.
