diff --git a/app/javascript/mastodon/components/icon.tsx b/app/javascript/mastodon/components/icon.tsx
index dbf5839606597b24e7525a8a20ec92bd38ea5897..f0af11f7f69f5f1626869cc3735e58ce991b473d 100644
--- a/app/javascript/mastodon/components/icon.tsx
+++ b/app/javascript/mastodon/components/icon.tsx
@@ -2,6 +2,8 @@ import classNames from 'classnames';
 
 import { ReactComponent as CheckBoxOutlineBlankIcon } from '@material-symbols/svg-600/outlined/check_box_outline_blank.svg';
 
+import { isProduction } from 'mastodon/utils/environment';
+
 interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> {
   title?: string;
 }
@@ -24,7 +26,7 @@ export const Icon: React.FC<Props> = ({
 }) => {
   // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
   if (!IconComponent) {
-    if (process.env.NODE_ENV !== 'production') {
+    if (!isProduction()) {
       throw new Error(
         `<Icon id="${id}" className="${className}"> is missing an "icon" prop.`,
       );
diff --git a/app/javascript/mastodon/components/router.tsx b/app/javascript/mastodon/components/router.tsx
index fe50fc2ba9014e56ec5d88420e55466c8a21cf42..42b926659cb71365094830999a9e1d4d04ac2e0d 100644
--- a/app/javascript/mastodon/components/router.tsx
+++ b/app/javascript/mastodon/components/router.tsx
@@ -11,6 +11,7 @@ import type {
 import { createBrowserHistory } from 'history';
 
 import { layoutFromWindow } from 'mastodon/is_mobile';
+import { isDevelopment } from 'mastodon/utils/environment';
 
 interface MastodonLocationState {
   fromMastodon?: boolean;
@@ -40,7 +41,7 @@ function normalizePath(
   } else if (
     location.state !== undefined &&
     state !== undefined &&
-    process.env.NODE_ENV === 'development'
+    isDevelopment()
   ) {
     // eslint-disable-next-line no-console
     console.log(
diff --git a/app/javascript/mastodon/containers/mastodon.jsx b/app/javascript/mastodon/containers/mastodon.jsx
index 59efc80570dc6cf468e2326ef35bdc95654263cf..87708da191bb56d95cdcb57b7ce964cb55d21f78 100644
--- a/app/javascript/mastodon/containers/mastodon.jsx
+++ b/app/javascript/mastodon/containers/mastodon.jsx
@@ -17,8 +17,9 @@ import UI from 'mastodon/features/ui';
 import initialState, { title as siteTitle } from 'mastodon/initial_state';
 import { IntlProvider } from 'mastodon/locales';
 import { store } from 'mastodon/store';
+import { isProduction } from 'mastodon/utils/environment';
 
-const title = process.env.NODE_ENV === 'production' ? siteTitle : `${siteTitle} (Dev)`;
+const title = isProduction() ? siteTitle : `${siteTitle} (Dev)`;
 
 const hydrateAction = hydrateStore(initialState);
 
diff --git a/app/javascript/mastodon/locales/global_locale.ts b/app/javascript/mastodon/locales/global_locale.ts
index 2d4329c7645023321e460447268faebd854b4beb..8d142b8b0492245670ced7fdfe3866ad554bee77 100644
--- a/app/javascript/mastodon/locales/global_locale.ts
+++ b/app/javascript/mastodon/locales/global_locale.ts
@@ -1,3 +1,5 @@
+import { isDevelopment } from 'mastodon/utils/environment';
+
 export interface LocaleData {
   locale: string;
   messages: Record<string, string>;
@@ -11,7 +13,7 @@ export function setLocale(locale: LocaleData) {
 
 export function getLocale(): LocaleData {
   if (!loadedLocale) {
-    if (process.env.NODE_ENV === 'development') {
+    if (isDevelopment()) {
       throw new Error('getLocale() called before any locale has been set');
     } else {
       return { locale: 'unknown', messages: {} };
diff --git a/app/javascript/mastodon/locales/intl_provider.tsx b/app/javascript/mastodon/locales/intl_provider.tsx
index 4fa8b2247c7cb26edd3b749b852250e3c9f24c37..68d4fcbd965191c9864eeead1c4bf1cf3a0c97a3 100644
--- a/app/javascript/mastodon/locales/intl_provider.tsx
+++ b/app/javascript/mastodon/locales/intl_provider.tsx
@@ -2,12 +2,14 @@ import { useEffect, useState } from 'react';
 
 import { IntlProvider as BaseIntlProvider } from 'react-intl';
 
+import { isProduction } from 'mastodon/utils/environment';
+
 import { getLocale, isLocaleLoaded } from './global_locale';
 import { loadLocale } from './load_locale';
 
 function onProviderError(error: unknown) {
   // Silent the error, like upstream does
-  if (process.env.NODE_ENV === 'production') return;
+  if (isProduction()) return;
 
   // This browser does not advertise Intl support for this locale, we only print a warning
   // As-per the spec, the browser should select the best matching locale
diff --git a/app/javascript/mastodon/main.jsx b/app/javascript/mastodon/main.jsx
index cd73cb572e1d12980ed745e38669046082534acb..e7979d56a1c8c8d3436a15fb4a6e41c9790eaa12 100644
--- a/app/javascript/mastodon/main.jsx
+++ b/app/javascript/mastodon/main.jsx
@@ -7,6 +7,8 @@ import * as perf from 'mastodon/performance';
 import ready from 'mastodon/ready';
 import { store } from 'mastodon/store';
 
+import { isProduction } from './utils/environment';
+
 /**
  * @returns {Promise<void>}
  */
@@ -21,7 +23,7 @@ function main() {
     root.render(<Mastodon {...props} />);
     store.dispatch(setupBrowserNotifications());
 
-    if (process.env.NODE_ENV === 'production' && me && 'serviceWorker' in navigator) {
+    if (isProduction() && me && 'serviceWorker' in navigator) {
       const { Workbox } = await import('workbox-window');
       const wb = new Workbox('/sw.js');
       /** @type {ServiceWorkerRegistration} */
diff --git a/app/javascript/mastodon/performance.js b/app/javascript/mastodon/performance.js
index 42849c82b103783746710c37e6092355723bb6b6..3bca95e85e63d6ae397edf2dc1990d958f579dd6 100644
--- a/app/javascript/mastodon/performance.js
+++ b/app/javascript/mastodon/performance.js
@@ -5,7 +5,9 @@
 
 import * as marky from 'marky';
 
-if (process.env.NODE_ENV === 'development') {
+import { isDevelopment } from './utils/environment';
+
+if (isDevelopment()) {
   if (typeof performance !== 'undefined' && performance.setResourceTimingBufferSize) {
     // Increase Firefox's performance entry limit; otherwise it's capped to 150.
     // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1331135
@@ -18,13 +20,13 @@ if (process.env.NODE_ENV === 'development') {
 }
 
 export function start(name) {
-  if (process.env.NODE_ENV === 'development') {
+  if (isDevelopment()) {
     marky.mark(name);
   }
 }
 
 export function stop(name) {
-  if (process.env.NODE_ENV === 'development') {
+  if (isDevelopment()) {
     marky.stop(name);
   }
 }
diff --git a/app/javascript/mastodon/utils/environment.ts b/app/javascript/mastodon/utils/environment.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b6371499f636945f0720846056c8d46cf44b0615
--- /dev/null
+++ b/app/javascript/mastodon/utils/environment.ts
@@ -0,0 +1,7 @@
+export function isDevelopment() {
+  return process.env.NODE_ENV === 'development';
+}
+
+export function isProduction() {
+  return process.env.NODE_ENV === 'production';
+}