{"slug":"bitfields-permissions-feature-flags","title":"Using Bitfields for Permissions and Feature Flags","date":"2025-09-03","description":"Learn how to use bitfields for efficient permission systems and feature flags with TypeScript","content":"\n# Introduction\n\nBitfields are a powerful technique for managing permissions and feature flags efficiently. I learned about bitfields since the beginning of my career back in late 2018, thanks to Discord's [permissions documentation](https://discord.com/developers/docs/topics/permissions#permissions).\n\nInstead of storing multiple boolean values or arrays, bitfields pack multiple flags into a single number using bitwise operations. This approach is memory-efficient, fast, and perfect for systems that need to check many permissions or features quickly.\n\n## How Bitfields Work\n\nA bitfield uses individual bits in a number to represent different flags. Each bit position represents a specific permission or feature:\n\n| Bit Position | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |\n|--------------|---|---|---|---|---|---|---|---|\n| Binary   | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |\n\nIn this example the decimal value `173` is represented as `10101101` in binary. Bits `0`, `2`, `3`, `5`, and `7` are set `(1)`, while bits `1`, `4`, and `6` are unset `(0)`.\n\n## Logic Gates and Bitwise Operations\n\nBitfields work using basic logic gates. Here's how each one works with truth tables:\n\n### AND (&) - Checks if a bit is set\nThe AND operation returns 1 only when both inputs are 1:\n\n| A | B | A & B |\n|---|---|-------|\n| 0 | 0 |   0   |\n| 0 | 1 |   0   |\n| 1 | 0 |   0   |\n| 1 | 1 |   1   |\n\n### OR (|) - Sets a bit\nThe OR operation returns 1 when at least one input is 1:\n\n| A | B | A \\| B |\n|---|---|--------|\n| 0 | 0 |   0    |\n| 0 | 1 |   1    |\n| 1 | 0 |   1    |\n| 1 | 1 |   1    |\n\n### XOR (^) - Toggles a bit\nThe XOR operation returns 1 when inputs are different:\n\n| A | B | A ^ B |\n|---|---|-------|\n| 0 | 0 |   0   |\n| 0 | 1 |   1   |\n| 1 | 0 |   1   |\n| 1 | 1 |   0   |\n\n### NOT (~) - Flips all bits\nThe NOT operation flips each bit (0 becomes 1, 1 becomes 0):\n\n| A | ~A |\n|---|----|\n| 0 | 1  |\n| 1 | 0  |\n\nProgrammatically you can use the following operations:\n\n```typescript\n// Example with 8-bit number: 10101101 (173)\nconst value = 173n; // Binary: 10101101\n\n// Check if bit 2 is set (AND operation)\nconst isBit2Set = (value & (1n << 2n)) !== 0n; // true\n\n// Set bit 1 (OR operation)\nconst setBit1 = value | (1n << 1n); // 175n (10101111)\n\n// Toggle bit 0 (XOR operation)\nconst toggleBit0 = value ^ (1n << 0n); // 172n (10101100)\n\n// remove bit 2\nconst removeBit2 = value & ~(1n << 2n); // 169n (10101001)\n```\n\n## Basic BitField Implementation\n\nHere's a simple BitField class for managing permissions:\n\n```typescript\nclass BitField {\n  private value: bigint;\n\n  constructor(value: bigint | string | number = 0n) {\n    this.value = BigInt(value);\n  }\n\n  // Add a permission (set bit)\n  add(permission: bigint): this {\n    this.value |= (1n << permission);\n    return this;\n  }\n\n  // Remove a permission (clear bit)\n  remove(permission: bigint): this {\n    this.value &= ~(1n << permission);\n    return this;\n  }\n\n  // Toggle a permission (flip bit)\n  toggle(permission: bigint): this {\n    this.value ^= (1n << permission);\n    return this;\n  }\n\n  // Check if permission exists (check bit)\n  has(permission: bigint): boolean {\n    return (this.value & (1n << permission)) !== 0n;\n  }\n\n  // Check if all permissions exist\n  hasAll(permissions: bigint[]): boolean {\n    return permissions.every(permission => this.has(permission));\n  }\n\n  // Check if any permission exists\n  hasSome(permissions: bigint[]): boolean {\n    return permissions.some(permission => this.has(permission));\n  }\n\n  // Get all set permissions\n  toArray(): bigint[] {\n    const permissions: bigint[] = [];\n    let temp = this.value;\n    let position = 0n;\n\n    while (temp > 0n) {\n      if (temp & 1n) {\n        permissions.push(position);\n      }\n      temp >>= 1n;\n      position++;\n    }\n\n    return permissions;\n  }\n\n  // Serialize to string for storage/transfer\n  toString(): string {\n    return this.value.toString();\n  }\n\n  // Create from string\n  static fromString(value: string): BitField {\n    return new BitField(value);\n  }\n}\n```\n\n## Permission System Example\n\n```typescript\n// Define permission constants using bit shifts\nconst PERMISSIONS = {\n  READ: 1n << 0n,      // 1n\n  WRITE: 1n << 1n,     // 2n\n  DELETE: 1n << 2n,    // 4n\n  ADMIN: 1n << 3n,     // 8n\n  MODERATE: 1n << 4n,  // 16n\n} as const;\n\n// Create user permissions\nconst userPermissions = new BitField()\n  .add(PERMISSIONS.READ)\n  .add(PERMISSIONS.WRITE);\n\n// Check permissions\nconsole.log(userPermissions.has(PERMISSIONS.READ));  // true\nconsole.log(userPermissions.has(PERMISSIONS.DELETE)); // false\n\n// Add admin permission\nuserPermissions.add(PERMISSIONS.ADMIN);\n\n// Check multiple permissions\nconsole.log(userPermissions.hasAll([PERMISSIONS.READ, PERMISSIONS.WRITE])); // true\n\n// Serialize for database storage\nconst serialized = userPermissions.toString(); // \"11\"\n```\n\n## Feature Flags Example\n\n```typescript\nconst FEATURES = {\n  DARK_MODE: 1n << 0n,\n  PREMIUM_CONTENT: 1n << 1n,\n  BETA_FEATURES: 1n << 2n,\n  ANALYTICS: 1n << 3n,\n  NOTIFICATIONS: 1n << 4n,\n} as const;\n\nclass User {\n  private features: BitField;\n\n  constructor(features: string = \"0\") {\n    this.features = BitField.fromString(features);\n  }\n\n  enableFeature(feature: bigint): void {\n    this.features.add(feature);\n  }\n\n  disableFeature(feature: bigint): void {\n    this.features.remove(feature);\n  }\n\n  hasFeature(feature: bigint): boolean {\n    return this.features.has(feature);\n  }\n\n  getFeatures(): string {\n    return this.features.toString();\n  }\n}\n\n// Usage\nconst user = new User();\nuser.enableFeature(FEATURES.DARK_MODE);\nuser.enableFeature(FEATURES.PREMIUM_CONTENT);\n\nconsole.log(user.hasFeature(FEATURES.DARK_MODE)); // true\nconsole.log(user.getFeatures()); // \"3\"\n```\n\n## Advantages and Disadvantages\n\n### Advantages\n\n| Advantage | Description |\n|-----------|-------------|\n| **Memory Efficient** | Single number instead of multiple booleans |\n| **Fast Operations** | Bitwise operations are extremely fast |\n| **Easy Serialization** | Single value to store/transfer |\n| **Atomic Operations** | All flags updated together |\n\n### Disadvantages\n\n| Disadvantage | Description |\n|--------------|-------------|\n| **Not Human Readable** | Requires bit manipulation knowledge |\n| **Debugging Difficulty** | Hard to understand raw values |\n| **Memory Usage** | Can use significant memory with many flags |\n\n## When to Use Bitfields\n\n**Use bitfields when:**\n- You have many boolean flags (10+)\n- Performance is critical\n- You need atomic flag operations\n- Memory usage matters\n- Flags are frequently checked together\n\n**Don't use bitfields when:**\n- You have few flags (< 5)\n- Flags have complex relationships\n- You need human-readable configuration\n- Flags change frequently during runtime\n\n## Important Notes\n\n**Always use `bigint` type** to declare bitfields and serialize it to a string to store it or transfer it over the wire because JavaScript numbers are limited to 32-bit bitwise operations.\n\n```typescript\n// ❌ Wrong - limited to 32-bit operations\nconst permissions: number = 1 << 32; // 1 (wraps around)\n\n// ✅ Correct - use bigint for unlimited precision\nconst permissions: bigint = 1n << 32n; // 4294967296n (exact)\n```\n\n## Conclusion\n\nBitfields are a powerful tool for managing permissions and feature flags efficiently. They provide excellent performance and memory usage at the cost of some readability. Use them when you need to manage many boolean flags and performance is important.\n\nThe key is to use `bigint` for large bitfields, serialize to strings for storage, and use bit shift operations (`1 << n`) instead of magic numbers for better maintainability.\n","readingTime":"6","tags":[]}