← Back to Blog
PHP Python

Idempotency

Sean Breeden March 5, 2026 5 min read
Idempotency

Idempotency


The “Do It Again, Nothing Changes” Principle


The Core Idea


Idempotency means: performing an operation multiple times produces the same result as doing it once.

The word comes from Latin: idem (same) + potent (power). Same power, no matter how many times you apply it.


The mental hook: Think of a light switch vs. a light toggle. Pressing a dedicated “ON” button ten times still just leaves the light on — that’s idempotent. Pressing a toggle ten times leaves you with an unpredictable state — that’s not.


The Everyday Analogy

Setting your thermostat to 72° F is idempotent. You can “set it to 72° F” five times in a row, and you always end up at 72° F. Compare that to “raise the temperature by 2° F”. Do that five times, and you’re at 82° F. One is safe to repeat. The other is dangerous to repeat.


Python Examples


NOT Idempotent - Appending to a list

results = []


def add_user(user):

results.append(user) # Each call changes state


add_user("sean")

add_user("sean")

add_user("sean")


print(results) # ["sean", "sean", "sean"] - different every time


Idempotent - Using a set, or checking first

results = set()


def add_user(user):

results.add(user) # Repeating has no extra effect


add_user("sean")

add_user("sean")

add_user("sean")


print(results) # {"sean"} - always the same


Idempotent - File Writing

# NOT idempotent - appends grow the file every run

with open("log.txt", "a") as f:

f.write("Server started\n")


# IDEMPOTENT - overwrite always produces the same file

with open("config.txt", "w") as f:

f.write("debug=true\n")


Idempotent - Database Upsert Pattern

# NOT idempotent - creates duplicate rows

def create_user(db, email):

db.execute("INSERT INTO users (email) VALUES (?)", (email,))


# IDEMPOTENT - safe to call repeatedly

def upsert_user(db, email):

db.execute("""

INSERT INTO users (email) VALUES (?)

ON CONFLICT(email) DO NOTHING

""", (email,))


Idempotent - Using dict.update() carefully

config = {"theme": "dark", "lang": "en"}


# Idempotent - applying the same patch always yields the same result

def apply_defaults(cfg):

cfg.setdefault("lang", "en")

cfg.setdefault("timeout", 30)


apply_defaults(config)

apply_defaults(config) # Safe - no double-setting


PHP Examples


NOT Idempotent — Incrementing a counter naively

function recordVisit($userId) {

$db->query("UPDATE users SET visits = visits + 1 WHERE id = $userId");

// Calling this twice double-counts!

}


Idempotent — Using a unique event log

function recordVisit($userId, $sessionId) {

$db->query("

INSERT IGNORE INTO visits (user_id, session_id)

VALUES (?, ?)

", [$userId, $sessionId]);

// Same session_id = no duplicate row

}


Idempotent — array_unique style deduplication

// NOT idempotent

$tags[] = "php"; // Adds every time


// IDEMPOTENT

$tags = array_unique([...$tags, "php"]); // Always the same result


Idempotent — File/Directory Creation

// NOT idempotent — throws if dir already exists

mkdir("/var/app/cache");


// IDEMPOTENT — safe to run in setup scripts every deploy

if (!is_dir("/var/app/cache")) {

mkdir("/var/app/cache", 0755, true);

}

// Or simply:

@mkdir("/var/app/cache", 0755, true); // suppress "already exists" error


Idempotent — HTTP Verbs (Critical for APIs)

// PUT is idempotent — replaces the whole resource

// Calling this 10 times = same end state

$app->put('/users/{id}', function($id, $data) {

User::updateOrCreate(['id' => $id], $data);

});


// POST is NOT idempotent — creates a new resource each time

$app->post('/users', function($data) {

User::create($data); // 10 calls = 10 new users

});


The “Why Should I Care?” Summary


Here are the real-world scenarios where idempotency saves you:


Retry logic / network failures: If a request fails mid-flight, can you safely retry it?

Database migrations: Can you run your migration script twice without breaking things?

Deployment scripts: Can you re-run setup without duplicating data or crashing?

Message queues: If a message is delivered twice, does your handler blow up?

REST API design: Is your PUT truly replacing, or accidentally appending?


The One-Sentence Rule to Remember


If calling this function twice in a row is safe, it’s idempotent. If it’s dangerous, it’s not.”


Whenever you write a function that touches state, a database, a file, a list, ask yourself: what happens if this runs twice? That question alone will save you from a whole category of subtle, hard-to-debug bugs.


Claude


In this new age of AI, I think it's important to clearly call out when something is largely AI-generated. Big shout-out to Claude by Anthropic for writing this great explanation of idempotency and for having ethics around AI development.

About the Author

Sean Breeden is a Full Stack Developer specializing in Mage-OS, Shopify, Magento, PHP, Python, and AI/ML. With years of experience in e-commerce development, he helps businesses leverage technology to create exceptional digital experiences.