What Is Endianness? The Byte Order Rule Every Engineer Needs

- Endianness is the byte order rule for multi-byte integers: big-endian stores the most significant byte at the lowest address; little-endian stores it at the highest
- x86, x86-64, and ARM (most modern hardware including Apple Silicon) are little-endian; TCP/IP network byte order is big-endian, a convention from the 1970s the internet never changed
- High-level bitwise operators (
&,|,>>) work on the integer value and are endianness-agnostic; the bug appears when you drop to raw byte arrays or binary buffers - Whenever bytes cross a system boundary (network socket, binary file, shared memory), both sides must agree on byte order explicitly — implicit assumptions cause silent data corruption with no error thrown
- Use
htonl/ntohlin C,struct.pack('>I', n)in Python, orDataViewwithlittleEndian = falsein JavaScript when sending integers over a socket - UTF-16 encodes each character as a 16-bit unit and needs a Byte Order Mark (BOM) to declare endianness; ASCII is immune because every character fits in one byte
You store the number 0x12345678 in memory. Simple. Now: which byte goes first?
Not a trick question. Well, sort of. The answer depends on your CPU architecture, and getting it wrong corrupts data silently across network boundaries, file formats, and cross-platform binaries. That's endianness. Welcome to one of computing's most successfully defended arbitrary hills.
Which Byte Goes First? That's the Whole Problem.
A 32-bit integer takes up four bytes. When you write 0x12345678 to memory, the four bytes are 0x12, 0x34, 0x56, and 0x78. They have to go somewhere. The question is what order.
Big-endian puts the most significant byte first, at the lowest memory address. Like reading a number left-to-right on paper: the biggest digit comes first. Humans invented this. We like the most important thing up front.
Little-endian puts the least significant byte first. The smallest piece of the number goes at the lowest address. This feels backwards until you realize there are legitimate hardware reasons for it, after which you accept it and move on with your life.
Here's what that looks like for 0x12345678 starting at address 0x1000:
| Address | Big-endian | Little-endian |
|---|---|---|
| 0x1000 | 0x12 | 0x78 |
| 0x1001 | 0x34 | 0x56 |
| 0x1002 | 0x56 | 0x34 |
| 0x1003 | 0x78 | 0x12 |
The integer value is identical. The memory layout is a mirror image.

When little-endian has its way with your carefully ordered bytes.
The Egg War That Named This
Jonathan Swift's Gulliver's Travels. The Lilliputians went to war over which end of a boiled egg to crack open. Big-Endians insisted on the big end. Little-Endians the little end. Neither side was objectively right. They couldn't agree, so they fought about it with actual armies.
Danny Cohen borrowed the metaphor in his 1980 paper "On Holy Wars and a Plea for Peace," arguing that byte order, like egg ends, was an arbitrary choice people had dressed up as principle. The names stuck because they're accurate: it really is that arbitrary. Nobody is right. Everybody insists they are. This has been the state of systems programming since 1980 and there is no sign of improvement.
Your CPU Is Probably Little-Endian
Most of the hardware you touch is little-endian.
x86 and x86-64 (every Intel and AMD processor in your laptop or server): little-endian. ARM in most configurations: little-endian, covering every modern smartphone and Apple Silicon Mac. RISC-V: little-endian by default.
Big-endian survives where it was standardized decades ago and nobody has had the political will to change it:
TCP/IP network byte order is big-endian. This was codified when big-endian architectures dominated, and the internet never changed it. Every IP address, port number, and TCP packet header on the wire is transmitted most-significant-byte first. Java's JVM uses big-endian internally regardless of the machine underneath, so Java's DataInputStream reads numbers in network byte order by default. IBM mainframes (z/Architecture) are big-endian.
The practical upshot: when you send a 32-bit integer from an x86 machine over a TCP socket, you need to swap its bytes. The C standard library has htonl() (host-to-network long) and ntohl() (network-to-host long) exactly for this. You call them, you move on, and you try not to think too hard about the fact that the entire internet standardized on the one byte order your CPU doesn't use.
At Runtime, Read the Lowest Address
You rarely need to check at runtime. Your compiler or standard library handles it. But you can, and understanding the mechanism is the interview-level insight.
In C, the classic trick uses a union that overlaps a uint32_t with a byte array:
#include <stdio.h> #include <stdint.h> int main() { union { uint32_t value; uint8_t bytes[4]; } test; test.value = 0x12345678; if (test.bytes[0] == 0x12) { printf("Big-endian\n"); } else { printf("Little-endian\n"); // prints on x86 } return 0; }
bytes[0] is always the lowest address. On a big-endian machine it holds 0x12 (the most significant byte). On a little-endian machine it holds 0x78.
In Python, the struct module gives you explicit control over byte order:
import struct, sys value = 0x12345678 big = struct.pack('>I', value) # b'\x12\x34\x56\x78' little = struct.pack('<I', value) # b'\x78\x56\x34\x12' print(big.hex()) # 12345678 print(little.hex()) # 78563412 print(sys.byteorder) # 'little' on x86/ARM
In TypeScript, the DataView API lets you read bytes in either order from an ArrayBuffer:
const buffer = new ArrayBuffer(4); const view = new DataView(buffer); view.setUint32(0, 0x12345678, false); // false = big-endian console.log(view.getUint8(0).toString(16)); // "12" view.setUint32(0, 0x12345678, true); // true = little-endian console.log(view.getUint8(0).toString(16)); // "78"
Why Endianness Shows Up in Coding Interviews
Three scenarios come up, and they all involve bytes crossing a boundary where both sides silently assumed the other side would cooperate.
Bit manipulation problems. When you work with individual bytes of a multi-byte integer, byte order determines which byte you're touching in raw memory. High-level bitwise operators (&, |, ^, shifts) operate on the integer value, not its memory layout. This is usually fine. The trap is when you drop from the value level to the byte level, like reading raw bytes out of a buffer or packet. For a deeper look at how bit operators work on integers, the bit manipulation cheat sheet covers the patterns that come up most.
Serialization and binary protocols. Any question about designing a wire format, or parsing one, involves byte order. The canonical answer: pick big-endian (network byte order) in your protocol spec, enforce it at the sender and receiver, and never leave it implicit. The Go standard library's encoding/binary package, Python's struct, and Java's DataInputStream all let you specify the order explicitly for exactly this reason.
Checksums and data integrity. If you compute a hash over raw bytes and two systems interpret the same integer with different byte orders, they compute different hashes over the "same" data. No error message. No exception. Just wrong answers when the two sides try to verify each other. The bug is invisible until someone squints at a checksum a week later and everything is on fire.
None of these require memorizing which CPU brands are big-endian. What you need is the mental model: integers have a canonical numeric value, but their byte layout is architecture-dependent, and any time bytes cross a boundary both sides must agree on byte order explicitly.
The Trap: Confusing Value Operations with Memory Layout
This is where engineers get tripped up, usually around midnight, usually in production. Consider:
n = 256 print(n & 1) # 0 -- correct, no endianness involved print(n >> 8) # 1 -- also correct
Bitwise operations in Python, Java, JavaScript, and every other high-level language work on the integer as a number. You cannot accidentally get the wrong answer due to endianness when using & or >>. The endianness is invisible at this level.
The bite comes when you switch to raw bytes:
import struct n = 256 # = 0x00000100 little = struct.pack('<I', n) print(little[0]) # 0 (least significant byte) print(little[1]) # 1 (next byte holds the 256) big = struct.pack('>I', n) print(big[0]) # 0 print(big[2]) # 1 (third byte from the top)
If you read little and assume big-endian layout, you decode 0x00000001 instead of 0x00000100. Silent. Wrong. No crash. The data corruption propagates downstream until someone notices the numbers are subtly off in ways that should be impossible.

Your big-endian service and your little-endian service, trying to coordinate.
This same class of bug is why two's complement integer representation matters: the sign bit is always the most significant bit of the value, but on a little-endian machine that bit lives in the last byte in memory, not the first. Understanding that memory layout and numeric representation are two separate things is the insight that unlocks both. The two's complement article covers the representation side in depth.
Bi-Endian: When the Architecture Can't Commit
Some architectures support both modes. ARM is the most common example. The CPU can be configured at boot time to operate in either big-endian or little-endian mode. This is called bi-endian.
In practice, nearly all ARM devices today run in little-endian mode. Linux on ARM defaults to little-endian. iOS and Android are little-endian. The bi-endian capability mostly serves specialized embedded and networking applications where big-endian is mandated by legacy hardware or protocol requirements. ARM built in full flexibility and then used little-endian for everything anyway.
For interview purposes: assume little-endian unless the question specifies a network context or a specific big-endian platform. Bi-endian is a "good to know" detail, not a source of common interview bugs.
One Byte Has No Endianness
Endianness only applies to data wider than one byte. A uint8_t goes in one address. There's no order to decide.
This is why ASCII text has no endianness problem, but UTF-16 does. UTF-16 encodes code points as 16-bit units. Which byte of each 16-bit unit comes first? The BOM (Byte Order Mark, U+FEFF) at the start of a UTF-16 file exists to answer that question. Read the BOM wrong and every character in the file decodes to the wrong code point. The entire document is garbage. This is technically correct behavior by both parties.
The same logic applies to any multi-byte data: 16-bit integers, 32-bit floats, 64-bit doubles. One byte is always safe. More than one byte always needs a declared byte order. Don't assume your counterpart on the other end of the socket made the same assumption. They probably didn't. Their tests all pass too.
Three Rules That Cover 95% of Interview Questions
- Network byte order is big-endian. Use
htonl/ntohlin C,struct.pack('>I', n)in Python, orDataViewwithlittleEndian = falsein JavaScript when sending integers over a socket. - High-level bitwise operators are endianness-agnostic. Endianness matters when you drop to raw byte arrays or buffers, not when you use
&,|, or>>. - Any time bytes cross a system boundary, both sides must agree on byte order explicitly. Implicit assumptions are where every endianness bug starts.
If this kind of systems-level reasoning is what your target companies interview on, the best way to get comfortable is explaining it out loud under time pressure. SpaceComplexity runs voice-based mock interviews that surface exactly this kind of conceptual question and give rubric-level feedback on how clearly you communicated it. Try it before your next onsite.