DEV Community

Anton Ohorodnyk
Anton Ohorodnyk

Posted on • Updated on • Originally published at anton.ohorodnyk.name

Quick Review of the Most Popular Ways to Implement Flags

Introduction

There are many ways to store flags and use them for communication between client <-> backend service or service <-> service. In the article, we are going to review the most popular options and we will try to help to choose the correct way for the next project.

The most popular ways to work with flags are:

  1. Store in separate column in DB or separate property in a class/struct
  2. Store as an array or in a set
  3. Store as binary data in a variable

Options

Separate field

It means we would create a separate column in DB for each flag.

DB Example

In an example, is_new will be a flag.
| id | username | is_new |
|----|----------|--------|
| 1 | test1 | true |
| 2 | test2 | false |

JSON Example

In a code or a NoSQL database representation would be:

[
    {
        "id": 1,
        "username": "test1",
        "is_new": true
    },
    {
        "id": 2,
        "username": "test2",
        "is_new": false
    }
]
Enter fullscreen mode Exit fullscreen mode

Pros

  • Simple to read
  • Simple to understand
  • Self-documented
  • In a code it can represent as typed properties
  • Simple to convert between different representations, like struct/class -> JSON -> protobuf -> MsgPack -> DB row -> struct/class
  • Simple to use in search and aggregation requests in DB (for example by SQL)
  • Can be updated in DB in parallel, by UPDATE query

Cons

  • Takes up memory/storage space/traffic
  • Could be expensive in development or risky to remove in case of dynamically typed languages
  • Could be expensive in development to add or remove or update in case of microservice architecture
  • Could be expensive in development to remove from DB as a column

Use Cases

In my opinion, before using this approach, we should answer "yes" for all listed options in the checklist:

  • I agree to extend add and remove columns in your database (or add fields to the NoSQL database)?
  • I understand it could take up resources in DB will be used
  • I do not have memory and traffic sensitive clients or services

Store In Array

In the current implementation, we will store all flags as strings in an array.

DB Example

In a DB, we are going to store values in a separate table as one to many relationships.

Table user:
| id | username |
|----|----------|
| 1 | test1 |
| 2 | test2 |

Table user_flag
| id | user_id | flag |
|----|---------|---------|
| 1 | 1 | is_new |
| 2 | 1 | is_test |

Every user_flag row is linked to a row in user table by user_id field.
It can be optimized to not store the flag as a string, but in our current topic, it is not so important.

JSON Example

In a code or a NoSQL database representation would be:


[
    {
        "id": 1,
        "username": "test1",
        "flags": [
            "is_new",
            "is_test"
        ]
    },
    {
        "id": 2,
        "username": "test2",
        "flags": []
    }
]
Enter fullscreen mode Exit fullscreen mode

Pros

  • Simple to read
  • Simple to understand
  • Self-documented
  • Could be dynamically extended in a code (does not need to update code to add new flag)
  • Simple to convert between different representations, like struct/class -> JSON -> protobuf -> MsgPack -> DB row -> struct/class
  • Pretty simple to use in search and aggregation requests in DB (for example by SQL)
  • Can be updated in DB in parallel, by INSERT query and DELETE query

Cons

  • Takes up memory/storage space/traffic
  • Need to write additional code for set and unset and find functions
  • Could affect different limitation in case of many flags
  • Could affect performance, especially on a client-side

User Cases

In my opinion, before using this approach, we should answer "yes" for all listed options in the checklist:

  • I do not have performance and memory and traffic sensitive clients or services
  • I understand it could take up resources on all sides it would be used

Store In Bitmask

Bitmask is a way when we use bitwise operations to get access to specific flag. Every flag stores in binary data in memory, for example it can be stored in: byte, short, int, uint, long, []byte, []short, map[int]byte, etc.

To see code in action, simply read README.md from binflags library.

DB Example

In DB we can store bitmask in various types, like: TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT, BLOB types.
| id | username | flags |
|----|----------|--------|
| 1 | test1 | 1 |
| 2 | test2 | 0 |

In the current example, flags field has the type INT and the first bit is is_new flag.

JSON Example

In a code or a NoSQL database representation would be:


[
    {
        "id": 1,
        "username": "test1",
        "flags": 1
    },
    {
        "id": 2,
        "username": "test2",
        "flags": 0
    }
]
Enter fullscreen mode Exit fullscreen mode

Pros

  • Simple to understand
  • The fastest implementation for all operations with flags
  • Do not take up additional memory and traffic resources (always only 1 bit per flag)
  • Can be used in a search by DB (but cannot be efficiently used indexes)

Cons

  • In some cases flags cannot be updated in parallel
  • Cannot search flags by index
  • Have to be documented (name to bit mapping)
  • Should be explained for some people

Use Cases

In my opinion, before using this approach, we should answer "yes" for all listed options in the checklist:

  • I want to save memory/traffic/processor resources
  • I understand all limitations

Conclusion

As we can see, as usual, we do not have silver bullet for all use cases and systems. But the provided list of implementations can help to find the best solution for a specific project.

If you want to use the most efficient option in Go, I would suggest checking Go implementation of binary flags for various types.

Discussion (0)