skills/software-engineering/fp-immutability/SKILL.md
Apply immutability principles across languages for safer, more predictable code
npx skillsauth add pantheon-org/tekhne fp-immutabilityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Immutability is a cornerstone of functional programming where data cannot be modified after creation. Instead of changing existing values, you create new values. This approach eliminates entire classes of bugs related to shared mutable state, makes code easier to reason about, and enables safe concurrent programming.
Immutable data has several key benefits:
// MUTABLE APPROACH (avoid)
const user = {
name: 'Alice',
email: '[email protected]',
addresses: []
};
function addAddress(user, address) {
user.addresses.push(address); // Mutation!
return user;
}
// IMMUTABLE APPROACH (prefer)
const userImmutable = {
name: 'Alice',
email: '[email protected]',
addresses: []
};
function addAddressImmutable(user, address) {
return {
...user,
addresses: [...user.addresses, address]
};
}
// Usage
const user1 = { name: 'Bob', addresses: [] };
const user2 = addAddressImmutable(user1, '123 Main St');
console.log(user1.addresses.length); // 0 - unchanged
console.log(user2.addresses.length); // 1 - new object
// Updating nested structures
const state = {
user: {
profile: {
name: 'Charlie',
settings: {
theme: 'dark',
notifications: true
}
}
}
};
// MUTABLE (avoid)
function toggleNotificationsMutable(state) {
state.user.profile.settings.notifications = !state.user.profile.settings.notifications;
return state;
}
// IMMUTABLE (prefer)
function toggleNotificationsImmutable(state) {
return {
...state,
user: {
...state.user,
profile: {
...state.user.profile,
settings: {
...state.user.profile.settings,
notifications: !state.user.profile.settings.notifications
}
}
}
};
}
// Using Immer library for simpler deep updates
import { produce } from 'immer';
function toggleNotificationsImmer(state) {
return produce(state, draft => {
draft.user.profile.settings.notifications = !draft.user.profile.settings.notifications;
});
}
// Array operations immutably
const numbers = [1, 2, 3, 4, 5];
// Adding elements
const withSix = [...numbers, 6]; // [1, 2, 3, 4, 5, 6]
const withZero = [0, ...numbers]; // [0, 1, 2, 3, 4, 5]
// Removing elements
const withoutThree = numbers.filter(n => n !== 3); // [1, 2, 4, 5]
const withoutFirst = numbers.slice(1); // [2, 3, 4, 5]
const withoutLast = numbers.slice(0, -1); // [1, 2, 3, 4]
// Updating elements
const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10]
const updateThird = [
...numbers.slice(0, 2),
999,
...numbers.slice(3)
]; // [1, 2, 999, 4, 5]
// Object operations immutably
const person = {
firstName: 'John',
lastName: 'Doe',
age: 30
};
// Adding/updating properties
const updated = { ...person, age: 31, city: 'Boston' };
// Removing properties
const { age, ...withoutAge } = person;
// Merging objects
const contact = { email: '[email protected]', phone: '555-1234' };
const merged = { ...person, ...contact };
// Complex transformations
const users = [
{ id: 1, name: 'Alice', active: true },
{ id: 2, name: 'Bob', active: false },
{ id: 3, name: 'Charlie', active: true }
];
// Update specific user immutably
function updateUser(users, id, updates) {
return users.map(user =>
user.id === id ? { ...user, ...updates } : user
);
}
const updatedUsers = updateUser(users, 2, { active: true });
Persistent data structures share structure for efficiency:
# Elixir's persistent data structures share memory
defmodule Performance do
# Adding to head is O(1)
def prepend_efficient(list, item) do
[item | list]
end
# Appending to tail is O(n)
def append_slow(list, item) do
list ++ [item]
end
# Building lists efficiently: collect then reverse
def build_list_efficiently(n) do
0..n
|> Enum.reduce([], fn i, acc -> [i | acc] end)
|> Enum.reverse()
end
# Maps are persistent hash trees - efficient updates
def update_map_efficiently(map, key, value) do
Map.put(map, key, value) # O(log n)
end
end
# Structural sharing example
original_map = %{a: 1, b: 2, c: 3, d: 4}
updated_map = %{original_map | a: 100}
# Only the changed part is new; rest is shared
The guidance above uses JavaScript for the primary examples. For the same patterns in other languages and deeper material, see:
Treat data as values, not containers to mutate. Produce a new copy for every change and treat every argument as read-only. Know when not to: a local variable never shared outside its function is fine to mutate for a tight loop.
state.user.name = "x";return { ...state, user: { ...state.user, name: "x" } };const next = JSON.parse(JSON.stringify(state)); next.a.b = 1;const next = { ...state, a: { ...state.a, b: 1 } };Object.freeze it, or return a fresh copy per caller.tools
A skill that produces warnings but no errors.
testing
A well-formed example skill for testing the validator.
development
A skill with code blocks and imperative instructions for testing content and contamination analysis.
tools