My Pre-Deployment Checklist for JavaScript Apps: What I Check Before Production


 There is a dangerous moment in web development when everything works on localhost.

The page opens. The buttons respond. The database returns data. Authentication appears fine. After staring at the same project for days, the temptation is to push it online and call it finished.

I have learned not to trust that feeling.

Local development removes a lot of the conditions that real users introduce. A production deployment has a different domain, different environment variables, different network conditions, different devices and people who will click things in an order I never imagined.

So before I treat a JavaScript app as ready, I run through a checklist.

It is not a guarantee that nothing will break. Production has a way of finding what a checklist missed. The purpose is to stop avoidable mistakes from being the reason it breaks.

1. I build the production version locally

The development server is not the production build.

For a Vite project, I run:

npm run build

Then I pay attention to warnings instead of treating a successful build as the only signal that matters.

I look for unresolved imports, unusually large bundles, deprecated packages, undefined variables and code paths that only work in development.

If the framework provides a way to preview the production build locally, I use it.

A project can behave perfectly under hot reload and still fail after bundling.

2. I check every environment variable

Missing environment variables create some of the most boring and frustrating deployment bugs.

I make a list of what the app expects: Supabase URL, Supabase client key, API base URL, analytics ID, email-service configuration and feature flags.

Then I verify that the hosting platform contains the production values.

I also ask a more important question: which of these values are safe to expose to browser code?

Frontend environment variables are often bundled into client-side JavaScript. I never place a secret server key into the frontend simply because the variable is stored in a dashboard.

If something grants privileged access, it belongs behind a server-side boundary.

3. I test authentication using the production URL

Authentication is one of those systems that can look complete until the domain changes.

I test sign up, sign in, sign out, email confirmation, password reset, expired-session behavior, protected routes and what happens after refreshing a protected page.

I also verify allowed redirect URLs in the authentication provider.

A reset link pointing back to localhost is a classic sign that the deployment was rushed.

4. I test authorization, not just login

A user being authenticated does not mean the user should be able to access everything.

This distinction is especially important with a backend such as Supabase.

I test with at least two normal accounts where possible.

If User A owns a record, can User B change its ID in a request and read it? Can an ordinary account update a field that should be controlled by the system? Can a user delete another user's record? Can an unauthenticated visitor call an endpoint I assumed was protected?

I want the database or server authorization layer to reject forbidden operations. Hiding a button in React is not access control.

5. I remove development shortcuts

During development, I create shortcuts.

Maybe I hard-code a test user ID. Maybe a console log prints an entire response. Maybe a mock result is still enabled. Maybe I added a temporary bypass around validation so I could test another page.

Before deployment, I search for those shortcuts deliberately.

I often search the project for:

TODO
FIXME
console.log
localhost
test
dummy
mock

Not every result is a problem. The search simply reminds me to inspect them.

6. I make errors useful without leaking internals

A user does not need to see a raw database error.

At the same time, I need enough information to debug real failures.

So I separate user-facing messages from developer-facing logs.

The interface might say:

We couldn't save your changes. Please try again.

The application log can contain the actual error, request context and timestamp needed to investigate.

I avoid exposing stack traces, keys, internal table information or unnecessary implementation details to users.

7. I test slow and broken networks

My development connection is not every user's connection.

This matters even more for products used on mobile networks.

I use browser developer tools to simulate slower connections. I watch what happens when a request takes several seconds, an image loads slowly, a user submits a form twice, a request fails halfway through or the connection disappears.

Does the button remain clickable and create duplicate submissions?

Does the page look frozen?

Does a loading indicator appear?

Can the user try again safely?

Those details decide whether an app feels reliable.

8. I use an actual phone

Responsive mode in a desktop browser is useful. It is not the same as touching the application on a phone.

I test the real site on a small screen.

I check input fields, keyboard behavior, menus, buttons near screen edges, horizontal scrolling, modals, long text and loading states.

A button that looks perfectly placed with a mouse can be irritating with a thumb.

9. I inspect the browser console

Before deployment and again on the live site, I open the console.

I do not want obvious red errors sitting there unnoticed.

Some third-party scripts produce warnings I cannot control, but errors from my own application deserve attention.

I also check the Network tab for failed requests, unexpected status codes and resources that are much larger than they should be.

10. I review performance without chasing a perfect score

Performance tools are useful, but I do not turn them into a game where the goal is 100.

I care about the real experience.

I compress images. I avoid loading enormous assets before they are needed. I remove dependencies that provide little value. I look at what blocks the first useful render.

If a dashboard needs heavy data, I think about whether all of it must load immediately.

The point is not a trophy screenshot. The point is making the product feel responsive on the devices and networks people actually use.

11. I verify metadata and basic SEO for public pages

Not every application needs search traffic, but public pages should still be understandable.

I check a useful page title, a sensible meta description, one clear primary heading, canonical behavior, social-sharing metadata where useful, robots settings and sitemap availability for indexable content.

Private dashboards should not accidentally become the pages I am asking search engines to discover.

12. I test the most important user journey from beginning to end

I define the one action the product exists to help someone complete.

For a marketplace, maybe it is finding a listing and contacting a seller.

For an artist-profile platform, maybe it is creating a profile and sharing it.

For a payment product, maybe it is completing the intended payment flow.

Then I do that exact journey on production like a new user.

I do not skip steps because I know how the product works.

13. I know what I will do if deployment goes wrong

A rollback plan does not need to be complicated.

I want to know where the previous working deployment is, whether I can restore it quickly, whether database migrations are reversible, what changed in this release and where errors will appear.

When something breaks, panic makes bad technical decisions easier. A simple recovery path reduces that pressure.

Production is another stage of development

I used to think deployment was the finish line.

Now I think of it as a change of environment.

The application moves from the controlled world where I know every button to a world where other people have different browsers, slower connections, unexpected data and no idea what I intended.

That is why my checklist is less about perfection and more about assumptions.

Every item asks the same question in a different form:

What am I assuming will work simply because it worked for me?

I still ship. I do not want fear of production to turn into endless polishing.

But I want the bugs that reach production to be the difficult ones, not the ones I could have caught by spending twenty focused minutes checking the basics.

That is progress too.

Post a Comment

Previous Post Next Post

Contact Form