Introduction
Shipping to production for the first time is exhilarating. You've built something, tested it locally, and now you're ready to let real users interact with it.
But shipping without proper preparation is how weekend incidents happen. It's how security breaches start. It's how you end up getting paged at 2 AM with a database that's locked up or an API that's costing you $10,000 per hour.
This guide walks you through a 10-point pre-deployment checklist that will bulletproof your application. These aren't "nice to have" features. These are the baseline security, reliability, and observability practices that every production application needs.
By the end of this guide, you'll have a concrete checklist to run through before every production deployment.
Why a Pre-Deployment Checklist Matters
Many engineers ship first and think about operations later. The result? Applications that break in production, leak data, get hacked, or cost far more to run than expected.
A solid pre-deployment checklist catches 90% of common failure modes before they affect users. It takes about 2-4 hours per project to set up properly, and it saves you from weeks of firefighting.
The 10-Point Pre-Deployment Checklist
1. Hardened Authorization ✅
What it is: Authorization determines what authenticated users are allowed to do.
Every resource access must verify ownership on the backend. Never rely on the frontend to enforce permissions.
// ❌ BAD: Only frontend checks
if (loggedInUser.id === resource.ownerId) {
// Show the resource
}
// ✅ GOOD: Backend enforces authorization
app.get("/api/resources/:id", async (req, res) => {
const resource = await db.resources.findById(req.params.id);
// Check ownership on the backend
if (resource.ownerId !== req.user.id) {
return res.status(403).json({ error: "Unauthorized" });
}
res.json(resource);
});
Checklist item: Before deploying, verify that at least 3 critical resources enforce authorization on the backend. Try to access another user's data. It should fail.
2. Validation & Sanitization ✅
Never trust user input. Malicious or malformed data can crash your server or corrupt your database.
import { z } from "zod";
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
age: z.number().int().min(0).max(150),
});
app.post("/api/users", async (req, res) => {
const result = createUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ error: result.error });
}
// Process the validated data
const user = result.data;
});
3. Tighten CORS Policies ✅
Lock down your CORS configuration so that only your explicitly approved frontend domain can communicate with your backend APIs.
import cors from "cors";
app.use(cors({
origin: ["https://yourapp.com", "https://www.yourapp.com"],
credentials: true,
}));
Checklist item: Open your app in production and check the CORS headers. They should explicitly list your domain(s), never *.
4. Rate Limiting ✅
Set up rate limiting on all public API endpoints to throttle excessive traffic.
import rateLimit from "express-rate-limit";
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
});
app.use("/api/", limiter);
5. Expiring Security Links ✅
Force password reset tokens to expire within a short window—usually 15 to 30 minutes max.
const token = crypto.randomBytes(32).toString("hex");
const expiresAt = new Date(Date.now() + 30 * 60 * 1000); // 30 minutes
await db.resetTokens.create({
userId: user.id,
token: hashToken(token),
expiresAt,
});
6. Graceful Frontend Error Handling ✅
Implement global error catch-alls and friendly fallback screens.
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="error-container">
<h1>Something went wrong</h1>
<p>We've been notified. Please try refreshing the page.</p>
<button onClick={() => window.location.reload()}>Reload</button>
</div>
);
}
return this.props.children;
}
}
7. Strategic Database Indexing ✅
Build indexes on your most frequently queried fields. Don't index everything blindly, as each index adds write overhead.
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_status ON posts(status);
CREATE INDEX idx_posts_created_at ON posts(created_at DESC);
8. Cost-Effective Logging ✅
Set up structured logging for critical application lifecycles and errors, keeping log retention windows tight.
const logger = {
error: (message, context) => {
console.error(JSON.stringify({
level: "error",
timestamp: new Date().toISOString(),
message,
...context,
}));
},
};
9. Actionable Proactive Alerts ✅
Build automated system alerts that notify you instantly when error rates spike or latency hits an unacceptable ceiling.
const criticalAlerts = [
{
name: "API Error Rate",
condition: "error_rate > 5%",
notify: "slack #alerts",
},
{
name: "API Latency",
condition: "p95_latency > 5000ms",
notify: "slack #alerts",
},
];
10. Fail-Safe Rollback Strategy ✅
Adopt a deployment strategy like Blue-Green deployments, where you keep the old version running alongside the new one, allowing you to route traffic back instantly if things go sideways.
Complete Pre-Deployment Checklist (Printable)
☐ Authorization: Tested resource access control. Cannot view other users' data.
☐ Validation: All user inputs validated against schema. HTML sanitized.
☐ CORS: CORS policy locked down to specific domains, not '*'.
☐ Rate Limiting: Rate limits configured on public endpoints.
☐ Security Links: Password reset/magic links expire in 15-30 minutes.
☐ Error Handling: Global error boundaries in place. No stack traces exposed.
☐ Database Indexes: Top 5 queries use indexes. EXPLAIN ANALYZE verified.
☐ Logging: Structured logging for critical paths. Retention window set.
☐ Alerts: Error rate, latency, and uptime alerts configured.
☐ Rollback: Rollback procedure documented and tested.
Key Takeaways
- Security first — Authorization, validation, and CORS are non-negotiable
- Monitoring always — You can't fix what you can't see
- Plan for failure — Rollback procedures save you from disasters
- Incremental, not perfect — You don't need every feature on day one, but you need the critical ones
- Test in production (carefully) — Use feature flags and blue-green deployments to test safely
Ready to deploy? Run through the 10-point checklist above. Your future self will thank you when the 2 AM incident doesn't happen.
Ready to start building?
Explore the most comprehensive directory of APIs for Nigerian developers and find exactly what you need.
Browse the API Directory


