diff --git a/src/ApolloClient.ts b/src/ApolloClient.ts
--- a/src/ApolloClient.ts
+++ b/src/ApolloClient.ts
@@ -44,6 +44,10 @@ import {
   Observable,
 } from './util/Observable';
 
+import {
+  isProduction,
+} from './util/environment';
+
 import {
   WatchQueryOptions,
   SubscriptionOptions,
@@ -225,7 +229,7 @@ export default class ApolloClient {
     // Attach the client instance to window to let us be found by chrome devtools, but only in
     // development mode
     const defaultConnectToDevTools =
-      typeof process === 'undefined' || (process.env && process.env.NODE_ENV !== 'production') &&
+      !isProduction() &&
       typeof window !== 'undefined' && (!(window as any).__APOLLO_CLIENT__);
 
     if (typeof connectToDevTools === 'undefined') {
diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts
--- a/src/core/QueryManager.ts
+++ b/src/core/QueryManager.ts
@@ -49,6 +49,10 @@ import {
   createStoreReducer,
 } from '../data/resultReducers';
 
+import {
+  isProduction,
+} from '../util/environment';
+
 import maybeDeepFreeze from '../util/maybeDeepFreeze';
 
 import {
@@ -346,7 +350,7 @@ export class QueryManager {
             }
           } else {
             console.error('Unhandled error', apolloError, apolloError.stack);
-            if (process.env.NODE_ENV !== 'production') {
+            if (!isProduction()) {
               /* tslint:disable-next-line */
               console.info(
                 'An unhandled error was thrown because no error handler is registered ' +
diff --git a/src/data/readFromStore.ts b/src/data/readFromStore.ts
--- a/src/data/readFromStore.ts
+++ b/src/data/readFromStore.ts
@@ -27,7 +27,13 @@ import {
   ApolloReducerConfig,
 } from '../store';
 
-import { isEqual } from '../util/isEqual';
+import {
+  isEqual,
+} from '../util/isEqual';
+
+import {
+  isTest,
+} from '../util/environment';
 
 /**
  * The key which the cache id for a given value is stored in the result object. This key is private
@@ -141,7 +147,7 @@ true option set in Apollo Client. Please turn on that option so that we can accu
 match fragments.`);
 
       /* istanbul ignore if */
-      if (process.env.NODE_ENV !== 'test') {
+      if (!isTest()) {
         // When running tests, we want to print the warning every time
         haveWarned = true;
       }
diff --git a/src/util/environment.ts b/src/util/environment.ts
new file mode 100644
--- /dev/null
+++ b/src/util/environment.ts
@@ -0,0 +1,24 @@
+export function getEnv(): string {
+  if (typeof process !== 'undefined' && process.env.NODE_ENV) {
+    return process.env.NODE_ENV;
+  }
+
+  // default environment
+  return 'development';
+}
+
+export function isEnv(env: string): boolean {
+  return getEnv() === env;
+}
+
+export function isProduction(): boolean {
+  return isEnv('production') === true;
+}
+
+export function isDevelopment(): boolean {
+  return isEnv('development') === true;
+}
+
+export function isTest(): boolean {
+  return isEnv('test') === true;
+}
diff --git a/src/util/maybeDeepFreeze.ts b/src/util/maybeDeepFreeze.ts
--- a/src/util/maybeDeepFreeze.ts
+++ b/src/util/maybeDeepFreeze.ts
@@ -1,3 +1,7 @@
+import {
+  isDevelopment,
+  isTest,
+} from './environment';
 
 // taken straight from https://github.com/substack/deep-freeze to avoid import hassles with rollup
 function deepFreeze (o: any) {
@@ -16,7 +20,7 @@ function deepFreeze (o: any) {
 };
 
 export default function maybeDeepFreeze(obj: any) {
-  if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
+  if (isDevelopment() || isTest()) {
     return deepFreeze(obj);
   }
   return obj;

diff --git a/test/environment.ts b/test/environment.ts
new file mode 100644
--- /dev/null
+++ b/test/environment.ts
@@ -0,0 +1,77 @@
+import { assert } from 'chai';
+
+import { isEnv, isProduction, isDevelopment, isTest } from '../src/util/environment';
+
+describe('environment', () => {
+  let keepEnv: string;
+
+  beforeEach(() => {
+    // save the NODE_ENV
+    keepEnv = process.env.NODE_ENV;
+  });
+
+  afterEach(() => {
+    // restore the NODE_ENV
+    process.env.NODE_ENV = keepEnv;
+  });
+
+  describe('isEnv', () => {
+    it(`should match when there's a value`, () => {
+      [
+        'production',
+        'development',
+        'test',
+      ]
+        .forEach(env => {
+          process.env.NODE_ENV = env;
+          assert.isTrue(isEnv(env));
+        });
+    });
+
+    it(`should treat no proces.env.NODE_ENV as it'd be in development`, () => {
+      delete process.env.NODE_ENV;
+      assert.isTrue(isEnv('development'));
+    });
+  });
+
+  describe('isProduction', () => {
+    it('should return true if in production', () => {
+      process.env.NODE_ENV = 'production';
+      assert.isTrue(isProduction());
+    });
+
+    it('should return false if not in production', () => {
+      process.env.NODE_ENV = 'test';
+      assert.isTrue(!isProduction());
+    });
+  });
+
+  describe('isTest', () => {
+    it('should return true if in test', () => {
+      process.env.NODE_ENV = 'test';
+      assert.isTrue(isTest());
+    });
+
+    it('should return true if not in test', () => {
+      process.env.NODE_ENV = 'development';
+      assert.isTrue(!isTest());
+    });
+  });
+
+  describe('isDevelopment', () => {
+    it('should return true if in development', () => {
+      process.env.NODE_ENV = 'development';
+      assert.isTrue(isDevelopment());
+    });
+
+    it('should return true if not in development and environment is defined', () => {
+      process.env.NODE_ENV = 'test';
+      assert.isTrue(!isDevelopment());
+    });
+
+    it('should make development as the default environment', () => {
+      delete process.env.NODE_ENV;
+      assert.isTrue(isDevelopment());
+    });
+  });
+});
diff --git a/test/tests.ts b/test/tests.ts
--- a/test/tests.ts
+++ b/test/tests.ts
@@ -53,3 +53,4 @@ import './customResolvers';
 import './isEqual';
 import './cloneDeep';
 import './assign';
+import './environment'

Avoid using process.env.NODE_ENV
There's no such thing in a browser
