GUID and UUID are the same thing
Say this once and it saves a lot of confusion later: a GUID is a UUID. Both names
describe the same 128-bit identifier, with the same bit layout, the same version and variant fields and
the same canonical 8-4-4-4-12 hexadecimal text form. "Globally unique identifier" is Microsoft's term,
dating from COM in the early 1990s and still used throughout the Windows registry, .NET and SQL Server.
"Universally unique identifier" is the name in the IETF specification, most recently RFC 9562. A value
produced by Guid.NewGuid() in C# and a value produced by uuid4() in Python are
interchangeable.
What differs is everything around the value: the text formats each ecosystem prefers, the case convention, the byte order of the binary form, and the database types and functions available. This page is about that surrounding layer — the Microsoft-specific conventions, and the places where crossing between them and the rest of the world goes wrong.
For the identifier itself — how the version and variant bits work, when to prefer a random value over a time-ordered one, the collision arithmetic, and how a random key behaves inside a B-tree index — the UUID generator is this site's canonical reference and covers all of it in depth, including the time-ordered version 7 that this page recommends as the modern answer to sequential-key problems. Nothing here repeats that material; the two pages are meant to be read together.
The formats, and where each one is expected
.NET's Guid.ToString(string format) accepts five specifiers, and Windows tooling uses
several of them in different places. Getting the wrong one into the wrong system is a common and
irritating class of bug, because the value is right and only the wrapping is wrong.
| Specifier | Example shape | Where you meet it |
|---|---|---|
N | 32 digits, no separators | Compact storage, filenames, cache keys, URL path segments |
D | 8-4-4-4-12 with hyphens — the default | JSON, logs, almost everything outside Windows; this is the canonical form |
B | The D form wrapped in curly braces | The registry, COM class and interface IDs, Visual Studio project type GUIDs, many Win32 APIs |
P | The D form wrapped in parentheses | Rare; some older tooling and a few configuration formats |
X | A brace-wrapped C struct initialiser of hex literals | C and C++ source that declares a GUID constant directly |
The braced form is the one that leaks. Someone copies a class ID out of regedit, pastes it
into a JSON config, and a parser somewhere rejects it — or worse, stores it with the braces, so a later
lookup for the unbraced value silently finds nothing. Guid.Parse in .NET is generous and
accepts all five forms, which is convenient locally and hides the problem right up until the value
reaches a stricter system. Normalise to the plain D form the moment a value crosses out of
Windows-specific code.
Case is the second half of the same problem. Windows tooling has historically leaned uppercase —
registry keys, many Microsoft documentation examples, uuidgen.exe output — while the IETF
specification says lowercase is canonical and most non-Microsoft ecosystems follow it. The value is
identical either way, since hexadecimal is case-insensitive, but string comparison in a database column,
a dictionary key or a URL path is not. A system that writes A1B2… and later reads
a1b2… finds nothing at all, and the bug survives code review because both strings are
obviously the same GUID to a human. Normalise the case at the boundary, in one place, and never rely on
the two matching.
The .NET byte-order gotcha
This is the single most expensive GUID mistake, because the corrupted value is still a perfectly well-formed GUID and nothing errors. It just is not the same identifier any more.
The original COM GUID structure was a C struct with four members: a 32-bit
Data1, two 16-bit fields Data2 and Data3, and an 8-byte array
Data4. On x86 — little-endian — the first three members are stored least-significant byte
first in memory, while Data4, being a byte array, is stored in order. .NET's
System.Guid preserves that layout exactly for interoperability, so
Guid.ToByteArray() returns a mixed-endian array: the first four bytes
reversed, the next two reversed, the next two reversed, and the last eight untouched.
Everyone else uses plain big-endian. RFC 9562 defines the binary form as the bytes in the order they
appear in the text. Java's UUID, PostgreSQL's uuid type, Python's
uuid.bytes and virtually every wire protocol follow that. So the same identifier has two
different 16-byte representations depending on which side produced it:
| Representation | Bytes for 00112233-4455-6677-8899-aabbccddeeff |
|---|---|
| Text form | 00 11 22 33 | 44 55 | 66 77 | 88 99 | aa bb cc dd ee ff |
| RFC / big-endian bytes | 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff |
.NET ToByteArray() | 33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff |
Read the first eight bytes of each. The last eight are identical; the first eight are shuffled within
three groups. Now consider what happens when a .NET service writes raw GUID bytes into a
BINARY(16) column and a Java service reads them back and formats them as text: it gets
33221100-5544-7766-8899-aabbccddeeff. A valid-looking GUID. A different record. No
exception, no warning, and a bug that surfaces weeks later as "some rows can't be found".
Three ways to avoid it, in order of preference. Pass the text form across boundaries —
36 characters costs nothing in a JSON payload and the ambiguity disappears entirely; use the
JSON formatter to check a payload of them. Use a database
type that understands GUIDs — SQL Server's uniqueidentifier and PostgreSQL's
uuid both handle the conversion for their own drivers. Or convert explicitly
with a documented helper, in exactly one place in the codebase, and write a test that round-trips a known
value. The Inspect box above prints both byte orders side by side so you can check which one you are
actually looking at.
The same asymmetry explains a related surprise: new Guid(byte[]) in .NET interprets the
array in the mixed-endian layout, so constructing a Guid from bytes that came from a non-Windows system
silently produces the wrong identifier. Since .NET 5 there is a constructor overload taking an
isBigEndian flag, which is the right thing to reach for when the bytes came from elsewhere.
GUIDs in SQL Server: NEWID, NEWSEQUENTIALID and fragmentation
SQL Server has a native 16-byte uniqueidentifier type and two built-in generators, and the
choice between them has a larger effect on a database's health than almost any other single-column
decision.
NEWID() returns a random version 4 GUID. It can be used anywhere — in a
SELECT, as a column default, inside a function. NEWSEQUENTIALID() returns a
GUID guaranteed to be greater than any previously produced by that machine since Windows last started,
and it may only be used as a column default on a uniqueidentifier column. That
restriction is not arbitrary: the value comes from an internal sequence, so it is not something you can
request ad hoc.
The reason to care is the clustered index. In SQL Server the clustered index defines the physical order of the table's rows, and by default the primary key is clustered. Give that key random values and every insert lands in a random page: the page is usually full, so it splits into two half-full pages, the index physically grows faster than the data, fill factor drifts toward the middle, and — because any insert can touch any page — the entire index has to stay in memory to avoid a random read per row. Every non-clustered index carries the clustering key as its row locator, so all of them inherit the cost as well.
NEWSEQUENTIALID() fixes the placement problem: values ascend, inserts land at the end of the
index, pages fill cleanly and fragmentation stops being a maintenance job. It comes with two real costs.
The values are predictable — the next one is close to the last, so exposing them
publicly leaks record ordering and invites enumeration, which is why Microsoft's own documentation warns
against using them where secrecy matters. And the sequence restarts from a new random base
after a service or machine restart, so the ordering is only monotonic within a run; a restart introduces
one out-of-order jump, which fragments the index a little at that point.
Some practical positions worth knowing:
- A primary key does not have to be the clustered index. Declaring the GUID key as
PRIMARY KEY NONCLUSTEREDand clustering on anIDENTITYcolumn keeps the physical order sequential while the public identifier stays random. This is often the cleanest fix on an existing schema. - Sequential GUIDs must satisfy SQL Server's own comparison, which is not a plain byte comparison — see below. A "sequential GUID" generated by application code that ascends numerically will not ascend from the database's point of view, which is a well-worn way to implement the optimisation and get none of the benefit.
- Consider UUID v7 instead. A time-ordered value generated in application code gives
you sequential inserts, portability across engines, and no dependence on a machine-local sequence —
and it fits a
uniqueidentifiercolumn unchanged. The UUID generator produces them, and a ULID is the same idea with a shorter text form. - Never store a GUID as
NVARCHAR(36). That is 72 bytes plus overhead for a 16-byte value, repeated through every index.CHAR(36)is merely bad;uniqueidentifieris correct.
Why ORDER BY on a uniqueidentifier looks wrong
SQL Server does not compare uniqueidentifier values the way the text form reads. It
compares the last group of six bytes first, then the group before it, and so on backwards through the
five groups. The result is that a GUID whose text form sorts earlier can compare as larger, and that
neither alphabetical order nor a straightforward big-endian byte comparison predicts the outcome.
This trips up two things in particular. Pagination or a keyset cursor built on a GUID column returns an order that no other system reproduces — the same rows come back in a different sequence from the application's own sort. And any attempt to hand-roll ascending GUIDs must produce values that increase under that comparison, which means varying the trailing bytes, not the leading ones. If you need a deterministic, portable order, sort on a real timestamp column or use a time-ordered identifier whose text form and byte order agree.
Generating GUIDs in the Microsoft toolchain
Every layer has its own idiom, and they all produce the same kind of value — a random version 4 GUID — differing only in the text format they hand back.
| Context | Call | Notes |
|---|---|---|
| C# / .NET | Guid.NewGuid() | Version 4, from the OS CSPRNG. .ToString("D") is the default; "B" for the braced form |
| C# / .NET 9+ | Guid.CreateVersion7() | Time-ordered, for keys that would otherwise fragment an index |
| PowerShell | [guid]::NewGuid() | Or New-Guid; pipe to .Guid for the bare string |
| T-SQL | SELECT NEWID() | Returns uppercase text; NEWSEQUENTIALID() is default-only |
| Visual Studio | Tools → Create GUID | Offers the registry, IMPLEMENT_OLECREATE, static const and braced forms |
| Windows command line | uuidgen / powershell New-Guid | uuidgen -c gives uppercase |
| VBScript / Classic ASP | Scriptlet.TypeLib | Legacy; returns a braced, uppercase value with a trailing newline that needs trimming |
A note on Guid.Empty, since it causes more confusion than it should. It is the all-zero
value, 00000000-0000-0000-0000-000000000000 — the same thing the IETF specification calls
the nil UUID. Because Guid is a struct rather than a class, an unassigned field is not
null; it is Guid.Empty. So a mapping bug, a missing constructor assignment or a
deserialisation that skipped a property all produce rows containing a real-looking identifier that is
the same for every affected record. Check for Guid.Empty explicitly at your validation
boundary and answer 400 Bad Request rather than storing it, and
prefer Guid? where the value is genuinely optional so that "unset" and "zero" stay
distinguishable.
The other Microsoft-specific value worth recognising is a class ID. COM identifies
every registered class and interface by a GUID, which is how CoCreateInstance finds an
implementation and how the registry organises HKEY_CLASSES_ROOT\CLSID. Those values are
braced and uppercase by convention, they are meant to be stable forever, and they are the reason the
braced format exists at all. Visual Studio project type identifiers work the same way. If a GUID in a
configuration file is well-known enough to be searchable, it is almost certainly one of these rather
than a record identifier.
Crossing between Windows and everything else
Most real GUID problems are integration problems. A checklist, in the order these tend to bite:
- Send the hyphenated lowercase text form in JSON. No braces, no URN prefix, no binary. Both sides parse it without ambiguity. Test the round trip with the API tester against your own endpoint before assuming it works.
- Normalise case exactly once, on the way in. A single lowercase call at the edge is worth more than case-insensitive comparisons scattered through the codebase.
- Strip wrappers at the edge. Braces, parentheses and
urn:uuid:prefixes should never reach storage. The Inspect box above shows every wrapper for a given value. - Never move raw bytes across platforms without stating the endianness. If you must, document which order the field uses and test with a known value where the two differ visibly.
- Validate before querying. An invalid identifier should produce 400 Bad Request; a valid one that matches nothing should produce 404 Not Found. Conflating them makes debugging harder and can leak whether a record exists.
- Do not treat a GUID as a credential. Unguessable is not confidential — identifiers
in URLs are recorded in server logs, browser history and
Refererheaders. Authenticate the request; see the API authentication guide.
One last piece of trivia that occasionally matters when you are reading a value rather than generating
one: the character at the start of the fourth group encodes the variant, and for standard values it is
always 8, 9, a or b. A c or
d there marks the legacy Microsoft variant — an old, pre-standard layout — and
strict parsers on other platforms may reject it. GUIDs from modern Windows APIs use the standard
variant, so this really is only a concern for values dug out of very old systems.
Frequently asked questions
Is a GUID the same thing as a UUID?
Yes — the same 128-bit standard under two names. GUID is Microsoft's term (COM, the registry, .NET, SQL Server); UUID is the IETF's, most recently RFC 9562. Only the surrounding conventions differ. The UUID generator is the deeper reference for the identifier itself.
Why does .NET produce a different byte array?
Guid.ToByteArray() preserves the COM struct layout on a little-endian machine: the
first four bytes are reversed, then the next two, then the next two, and the final eight are left alone.
Everything else uses plain big-endian, so raw bytes crossing that boundary become a different, still
valid-looking GUID.
What is the braced format for?
Curly braces are the Windows convention — registry CLSID keys, COM class and interface IDs, Visual
Studio project type GUIDs. It is the B specifier in .NET. Strip the braces before sending a
value to a JSON API.
NEWID or NEWSEQUENTIALID?
NEWID() is random and can be called anywhere; NEWSEQUENTIALID() ascends and
may only be a column default. Random values fragment a clustered index badly; sequential values fix that
but are predictable and restart from a new base after a reboot.
Why does SQL Server sort uniqueidentifier so oddly?
It compares the last six bytes first and works backwards through the groups, so neither alphabetical order nor big-endian byte order predicts the result. Sort on a timestamp column if you need a portable order.
Are GUIDs case sensitive?
The value is not, but string comparison usually is. Windows tooling leans uppercase and the IETF specification says lowercase; pick one and normalise at every boundary.
What is Guid.Empty?
The all-zero GUID, identical to the nil UUID. Because Guid is a struct, an unassigned
field is Guid.Empty rather than null — which is why it turns up in databases as the
fingerprint of a mapping bug.
How should a GUID be stored in SQL Server?
In a uniqueidentifier column — 16 bytes. CHAR(36) more than doubles that
and NVARCHAR(36) quadruples it, in the clustered index and in every non-clustered index
that carries the key.
Are these GUIDs generated on a server?
No. They are created in your browser with crypto.getRandomValues and never transmitted.
Watch the network tab while pressing Generate — there are no requests at all.
Related tools and reference
UUID Generator
The canonical reference for the identifier itself — versions, variant bits, collisions and index behaviour.
ULID Generator
A time-ordered 128-bit alternative when sequential inserts matter more than the Microsoft text formats.
JSON Formatter
Check a payload full of identifiers before it crosses a platform boundary.
Random String Generator
For secrets and API keys, where a GUID is the wrong shape of value.
API Tester
Round-trip a GUID through your own endpoint and confirm the format survives.
404 Not Found
The right answer for a well-formed identifier that matches no record.