Hashing
String manipulation
Unique identifiers
Cryptography
Algorithm

make a unique hash out of two strings

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you need one stable identifier from two strings, the main challenge is not only picking a hash algorithm. You also need to combine the inputs in a way that is unambiguous, reproducible, and appropriate for your use case.

Hashes Are Stable, Not Truly Unique

A hash function converts arbitrary input into a fixed-size digest. In practice, good hash functions make collisions extremely unlikely, but no fixed-size hash can guarantee uniqueness for every possible pair of strings. That is why the right goal is usually a stable, low-collision identifier rather than a mathematically unique one.

For most application code, a cryptographic hash such as SHA-256 or BLAKE2 is a safe default. It is deterministic, widely available, and works well for IDs, deduplication keys, and signatures where accidental collisions must be rare.

Combine the Two Strings Safely First

The biggest mistake is naive concatenation. If you simply join two strings, different pairs can produce the same combined text. For example, "ab" + "c" and "a" + "bc" both become "abc".

A reliable fix is to prefix each string with its length before hashing:

python
1import hashlib
2
3def pair_hash(left: str, right: str) -> str:
4    payload = f"{len(left)}:{left}|{len(right)}:{right}"
5    return hashlib.sha256(payload.encode("utf-8")).hexdigest()
6
7print(pair_hash("ab", "c"))
8print(pair_hash("a", "bc"))

Those two calls produce different digests because the encoded payload is no longer ambiguous. The length prefix matters more than the visual separator. Even if a string contains | or :, the lengths still let you reconstruct the original pair unambiguously.

Decide Whether Order Matters

Sometimes the pair "alice", "bob" should be different from "bob", "alice". In that case, hash the inputs in their original order.

In other cases, the pair represents an unordered relationship, such as a direct message channel between two users. Then normalize the order first:

python
1import hashlib
2
3def unordered_pair_hash(a: str, b: str) -> str:
4    first, second = sorted([a, b])
5    payload = f"{len(first)}:{first}|{len(second)}:{second}"
6    return hashlib.sha256(payload.encode("utf-8")).hexdigest()
7
8print(unordered_pair_hash("alice", "bob"))
9print(unordered_pair_hash("bob", "alice"))

With normalization, both calls return the same digest. That small design choice changes the semantics of the hash, so decide it before storing anything in a database.

Do Not Use the Language's Built-In Object Hash for Persistent IDs

Many languages have a quick hash() function, but that is often the wrong tool for stable identifiers. For example, Python intentionally randomizes the hash of strings across processes to defend against hash-collision attacks. That makes built-in hashes unsuitable for values that must remain stable across runs, machines, or deployments.

Use a cryptographic library instead:

  • It is deterministic across processes.
  • It gives you a stable text representation such as hexadecimal.
  • It is explicit about encoding and digest length.

If you need a shorter key, consider BLAKE2 with a smaller digest size rather than truncating a weak algorithm.

A Good Mental Model

Think of the task as two steps:

  1. Serialize the pair unambiguously.
  2. Hash the serialized bytes with a stable algorithm.

Once you frame it that way, the implementation becomes much safer. The bugs rarely come from SHA-256 itself. They come from accidental ambiguity in how the pair was combined before hashing.

Common Pitfalls

Plain concatenation is the most common error because it silently creates collisions before the hash function even runs. Always add structure to the combined input.

Another mistake is ignoring character encoding. If one service uses UTF-8 and another uses a different encoding, the same text can hash to different values.

Be careful about order. If the pair is logically unordered, normalize it before hashing. If order is meaningful, do not sort the inputs.

Finally, do not promise absolute uniqueness. A hash drastically reduces collision risk, but it does not remove it completely. If collision resistance is business-critical, keep the original values available for verification.

Summary

  • A hash of two strings is only as reliable as the serialization step before hashing.
  • Avoid naive concatenation because different pairs can collapse into the same input text.
  • Use a stable algorithm such as SHA-256 or BLAKE2 for persistent identifiers.
  • Decide explicitly whether input order should matter before generating the digest.
  • Treat hashes as collision-resistant identifiers, not as perfect guarantees of uniqueness.

Course illustration
Course illustration

All Rights Reserved.