Files
nixpkgs/docs/reference/patterns.md
m3tm3re 00b858fbbe docs: update documentation for latest changes
- Add stt-ptt language support documentation
- Add rofi-project-opener module documentation
- Add rofi-project-opener package documentation
- Update zellij-ps documentation
- Update guides and reference patterns
- Update AGENTS.md with latest commands
2026-01-10 19:12:45 +01:00

7.7 KiB

Code Patterns and Anti-Patterns

Common code patterns and anti-patterns used in m3ta-nixpkgs.

Overview

This document outlines recommended patterns and common pitfalls when working with m3ta-nixpkgs.

Code Patterns

Package Pattern

CallPackage Registry

Use callPackage for lazy evaluation:

# Good - pkgs/default.nix
{
  inherit (pkgs) callPackage;
} rec {
  code2prompt = callPackage ./code2prompt {};
  mem0 = callPackage ./mem0 {};
}

Meta Fields

Always include complete meta information:

# Good
meta = with lib; {
  description = "My awesome package";
  homepage = "https://github.com/author/package";
  changelog = "https://github.com/author/package/releases/tag/v${version}";
  license = licenses.mit;
  platforms = platforms.linux;
  mainProgram = "program-name";
};

Module Pattern

Standard Module Structure

# Good
{ config, lib, pkgs, ... }:
with lib; let
  cfg = config.m3ta.myModule;
in {
  options.m3ta.myModule = {
    enable = mkEnableOption "my module";
    # ... options
  };

  config = mkIf cfg.enable {
    # ... configuration
  };
}

mkEnableOption

Always use mkEnableOption for enable flags:

# Good
options.m3ta.myModule = {
  enable = mkEnableOption "my module";
};

# Bad
options.m3ta.myModule.enable = mkOption {
  type = types.bool;
  default = false;
};

Conditional Configuration

Use mkIf for conditional config:

# Good
config = mkIf cfg.enable {
  services.my-service.enable = true;
};

Multiple Conditions

Use mkMerge for multiple conditions:

# Good
config = mkMerge [
  (mkIf cfg.feature1.enable {
    # config for feature1
  })
  (mkIf cfg.feature2.enable {
    # config for feature2
  })
];

Import Pattern

Multi-line Imports

Multi-line, trailing commas:

# Good
{
  lib,
  stdenv,
  fetchFromGitHub,
}:

Explicit Dependencies

# Good
{
  lib,
  stdenv,
  openssl,
  pkg-config,
}:
stdenv.mkDerivation {
  buildInputs = [openssl pkg-config];
}

Anti-Patterns

lib.fakeHash in Commits

Bad: Committing lib.fakeHash

# Bad - Never commit this!
src = fetchFromGitHub {
  hash = lib.fakeHash;
};

Solution: Build to get real hash:

nix build .#your-package
# Copy actual hash from error message

Flat Module Files

Bad: All modules in one file

# Bad - Hard to maintain
{config, lib, pkgs, ...}: {
  options.m3ta.cli = {
    tool1 = mkEnableOption "tool1";
    tool2 = mkEnableOption "tool2";
    # ... many more
  };

  config = mkMerge [
    (mkIf config.cli.tool1.enable {...})
    (mkIf config.cli.tool2.enable {...})
    # ... many more
  ];
}

Solution: Organize by category

# Good - modules/home-manager/cli/
# modules/home-manager/cli/default.nix
{
  imports = [
    ./tool1.nix
    ./tool2.nix
  ];
}

Hardcoded Ports

Bad: Hardcoding ports in services

# Bad
services.nginx = {
  enable = true;
  httpConfig = ''
    server {
      listen 80;
    }
  '';
};

Solution: Use port management

# Good
m3ta.ports = {
  enable = true;
  definitions = {nginx = 80;};
};

services.nginx = {
  enable = true;
  httpConfig = ''
    server {
      listen ${toString (config.m3ta.ports.get "nginx")};
    }
  '';
};

Skipping Meta Fields

Bad: Incomplete meta information

# Bad
meta = {
  description = "My package";
};

Solution: Include all fields

# Good
meta = with lib; {
  description = "My awesome package";
  homepage = "https://github.com/author/package";
  license = licenses.mit;
  platforms = platforms.linux;
  mainProgram = "program-name";
};

with pkgs; in Modules

Bad: Using with pkgs; at module level

# Bad
{config, lib, pkgs, ...}: with pkgs; {
  config.environment.systemPackages = [
    vim
    git
  ];
}

Solution: Explicit package references or limited with

# Good - Explicit references
{config, lib, pkgs, ...}: {
  config.environment.systemPackages = with pkgs; [
    vim
    git
  ];
}

# Or - Full references
{config, lib, pkgs, ...}: {
  config.environment.systemPackages = [
    pkgs.vim
    pkgs.git
  ];
}

Orphaned Package Directories

Bad: Creating directory without registering

# Bad - Package not visible
# pkgs/my-package/default.nix exists
# But pkgs/default.nix doesn't reference it

Solution: Register in pkgs/default.nix

# Good
{
  inherit (pkgs) callPackage;
} rec {
  my-package = callPackage ./my-package {};
}

Type Safety

No Type Suppression

Bad: Using as any

# Bad
let
  value = someFunction config as any;
in
  # ...

Solution: Fix underlying type issues

# Good
let
  value = someFunction config;
in
  # Ensure value has correct type

Proper Type Definitions

# Good
options.m3ta.myModule = {
  enable = mkEnableOption "my module";

  port = mkOption {
    type = types.port;
    default = 8080;
    description = "Port to run on";
  };
};

Naming Conventions

Package Names

Good: lowercase-hyphen

code2prompt
hyprpaper-random
launch-webapp

Bad: CamelCase or underscores

code2Prompt      # Bad
hyprpaper_random # Bad

Variables

Good: camelCase

portHelpers
configFile
serviceName

Bad: Snake_case or kebab-case

port_helpers    # Bad
port-helpers   # Bad

Module Options

Good: m3ta.* namespace for m3ta modules, cli.* namespace for CLI modules

m3ta.ports.enable = true;
cli.zellij-ps.enable = true;

Bad: Flat namespace

ports.enable = true;  # Potential conflict
zellij-ps.enable = true;  # Hard to find

Performance

Lazy Evaluation

Good: Use callPackage

# Good - Only builds requested package
code2prompt = callPackage ./code2prompt {};

Bad: Building all packages

# Bad - Builds everything even if not used
code2prompt = import ./code2prompt {};

Selective Imports

Good: Import only needed modules

# Good
imports = [
  m3ta-nixpkgs.nixosModules.mem0
];

Bad: Importing all modules

# Bad - Imports and evaluates all modules
imports = [
  m3ta-nixpkgs.nixosModules.default
];

Security

No Secrets in Store

Bad: Putting secrets in configuration

# Bad - Secret in Nix store
m3ta.mem0.llm.apiKey = "sk-xxx";

Solution: Use secret files

# Good - Secret from file
m3ta.mem0.llm.apiKeyFile = "/run/secrets/openai-api-key";

Proper User/Group

Good: Dedicated users for services

# Good
users.users.mem0 = {
  isSystemUser = true;
  group = "mem0";
};

Service Hardening

Good: Enable systemd hardening

# Good
systemd.services.mem0.serviceConfig = {
  NoNewPrivileges = true;
  PrivateTmp = true;
  ProtectSystem = "strict";
  ProtectHome = true;
};

Best Practices Summary

Practice Do Don't
Hash fetching Get real hash from build error Commit lib.fakeHash
Module organization Categorize by function Put all in one file
Port management Use m3ta.ports module Hardcode ports
Meta fields Include all fields Skip fields
Type safety Fix type errors Use as any
Dependencies Explicit declarations Implicit deps
Imports Multi-line, trailing comma Single-line, no comma
Naming Follow conventions Mix styles
Secrets Use file-based Put in config
Evaluation Lazy (callPackage) Import everything