DEV Community

Cover image for Debugging production with hidden modals
Martin Devillers
Martin Devillers

Posted on • Originally published at omi.cr

Debugging production with hidden modals

I've added a debug modal so I can experiment with some new features on the production version of the Big O Visualizer. This is especially useful on my iPhone where my debugging tools are limited. So where is this screen? Well, I hid it, because it's super secret and I don't want visitors to mess with these experimental options.

...

...

...

Ok, I'll tell you where it is. There's an invisible button to the right of the website's title at the top of the screen. If you click/tap it rapidly (about eight times in less than three seconds) the modal will appear.

You should totally do that!

If you think this is just some crazy feature I've implemented in my own pet-project, think again because I've been building these hidden debug tools into most of my former client's applications (and yes with their consent). Why? Because I'm a huge fan of Testing In Production (yay) and not-such-a-big-fan of But It Worked On Develop/Test/Acceptance/Demo/RC/Spike/Bla (boo).

The problem is that we tend to avoid building these tools into our products because they're of no value to our end-users. The fallacy is that the teams who build these products do not see themselves (or are not seen as) one of the most valuable end-users! So we limit these debug tools to non-production environments (or not build them at all), where they're of little value. Such a missed opportunity! So build debug windows and ship them proudly, like YouTube's video player:

Stats for nerds

Disclaimer: the options of these debug tools should be limited for (obvious) security reasons. Don't try to ship a hidden "Bypass Login Screen" or "Dump Database" option, because some hacker will definitely find it.

Custom React Hooks

All the settings are implemented as React Hooks which is a powerful and expressive new way to reuse functionality between components that was introduced with React 16.8.

Thanks to React Hooks, any component can use a setting with a simple one-liner: const [preanalyzedMode] = usePreanalyzedMode(), and then use the setting inside its useEffect method. Any changes to the setting will automagically propagate to the components, so they can update their state accordingly.

Each setting is a TypeScript enum that is persisted to the browser's localStorage. I adopted the useLocalStorage recipe from Gabe Ragland's excellent usehooks.com website and rolled into my own more TypeScripty useLocalStorageBackedEnum. This hook factory takes the name of the storage key, the enum used for the setting and a default value for the setting and returns a fully functional React Hook. The result looks like this:

import { Dispatch, useState } from "react"

function useLocalStorageBackedEnum<TEnum extends string, TEnumValue extends number | string>(
  key: string,
  enumType: { [key in TEnum]: TEnumValue },
  defaultValue: TEnumValue
): [TEnumValue, Dispatch<TEnumValue>] {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key)
      if (item === null) return defaultValue
      return (Number.isNaN(Number(item)) ? item : +item) as TEnumValue
    } catch (error) {
      console.log(error)
      return defaultValue
    }
  })

  const setValue = (value: TEnumValue) => {
    try {
      setStoredValue(value)
      window.localStorage.setItem(key, value.toString())
    } catch (error) {
      console.log(error)
    }
  }

  return [storedValue, setValue]
}
Enter fullscreen mode Exit fullscreen mode

This custom Hook makes introducing settings as easy as:

export enum PreanalyzedMode {
  Enabled = "enabled",
  Disabled = "disabled",
  Persist = "persist",
}

export enum WebWorkerMode {
  Enabled = 0,
  Disabled = 99999999999999,
  XLOnly = 1000000,
}

export enum StopwatchMode {
  None = "none",
  Analyzer = "analyzer",
  Algorithm = "algorithm",
}

export const usePreanalyzedMode = () =>
  useLocalStorageBackedEnum("preanalyzed-mode", PreanalyzedMode, PreanalyzedMode.Enabled)
export const useWebWorkerMode = () =>
  useLocalStorageBackedEnum("web-worker-mode", WebWorkerMode, WebWorkerMode.Disabled)
export const useStopwatchMode = () =>
  useLocalStorageBackedEnum("stopwatch-mode", StopwatchMode, StopwatchMode.None)
Enter fullscreen mode Exit fullscreen mode

I ā¤ React

Top comments (0)