How to Prepare a Vibe-Coded App for Its First Deployment
A first deployment should not begin by opening a hosting dashboard and clicking “New Project.”
The safer starting point is your local repository.
Before a deployment platform can run an application, the project must clearly describe what it needs, how it builds, how it starts, where it stores data, and which configuration values must be supplied. The application must also stop depending on files, credentials, tools, and assumptions that exist only on your computer.
This preparation is especially important for vibe-coded applications. An AI coding assistant can produce a working feature without documenting every dependency or operational assumption it introduced. It may install a package without updating the correct lockfile, read an API key from a local file, rely on development middleware, or create a database table manually rather than through a migration.
Those decisions can remain invisible while the application runs locally.
Your goal before the first deployment is not to make the application perfect. It is to make the project reproducible, configurable, reviewable, and safe enough to deploy into a controlled environment.
This article uses the same sample project as the previous guides:
Application: Appointment booking service
Frontend:
- React with Vite
Backend:
- Node.js with Express
Database:
- PostgreSQL
Additional processes:
- Hourly appointment reminder
- Background email worker
External services:
- Transactional email provider
Target workflow:
- Git repository
- Preview or staging deployment
- Production deployment laterThe examples are illustrative and have not been executed against your project. Adapt commands, filenames, ports, and framework settings to the application you are preparing.
What You Should Have After This Preparation
By the end of the process, you should have:
- A clean and understandable Git repository
- A reproducible dependency installation
- Confirmed build and start commands
- A documented runtime version
- An inventory of required environment variables
- No exposed production credentials
- A production-safe configuration
- A repeatable database migration process
- Basic server-side validation and authorization checks
- Useful application logs
- A health-check endpoint where appropriate
- A deployment verification plan
- A rollback starting point
These preparations do not make the application fully production-ready. They create a reliable foundation for the first deployment and the later launch work covered throughout this series.
Step 1: Create a Safe Git Starting Point
Before changing deployment-related files, confirm that the project is under version control and that the current working state is recorded.
Run these commands from the repository root:
git status
git branch --show-current
git log --oneline -5Review the results rather than assuming the repository is clean.
git status should show whether files are modified, untracked, or staged. The branch command should confirm that you are not accidentally working directly on an important production branch. The log should show whether recent working versions were committed.
Create a focused branch for deployment preparation:
git switch -c prepare-first-deploymentBefore committing anything, inspect untracked files carefully. Do not use a broad command such as git add . until you know that the repository does not contain local environment files, database exports, debug logs, credentials, or uploaded user files.
A safer initial review is:
git status --shortThen add reviewed files deliberately:
git add package.json package-lock.json
git add src
git add README.mdCommit a known-good state before large configuration changes:
git commit -m "Save working app before deployment preparation"A commit is not a backup of an external database, API configuration, or untracked local file. It gives you a reliable code reference from which you can inspect or reverse later edits.
Step 2: Remove Files That Should Not Be Deployed
A local project often accumulates files that should not enter Git or the deployment build context.
Common examples include:
.env
.env.local
.env.production
node_modules/
dist/
coverage/
*.log
.DS_Store
uploads/
database-backup.sqlThe correct exclusions depend on the framework and deployment process. Do not copy a generic .gitignore without reviewing it.
A basic Node.js example might contain:
node_modules/
dist/
coverage/
*.log
.env
.env.*
!.env.example
.DS_StoreThe exception for .env.example allows you to commit a template containing variable names and non-sensitive examples.
Adding a file to .gitignore does not remove it from Git if it has already been committed. Check tracked environment files with:
git ls-files | grep -E '(^|/)\.env'On PowerShell, an equivalent check is:
git ls-files | Select-String '(^|/)\.env'Also search for filenames that may contain backups or credentials:
git ls-files | grep -E '\.(pem|key|p12|sql|sqlite|db)$'Review every result in context. A migration .sql file may be intentional, while a production database export should not normally be committed.
OWASP recommends removing test code, unnecessary functionality, and sensitive information from production configuration and build artifacts. It also emphasizes secure defaults rather than requiring an operator to remember every hardening step later.
Step 3: Confirm the Runtime and Package Manager
Your computer may have several runtimes and package managers installed. A deployment platform needs an explicit, supported choice.
For a Node.js project, inspect:
node --version
npm --versionThen review the repository:
cat package.jsonOn PowerShell:
Get-Content package.jsonLook for:
- The
enginesfield - The
packageManagerfield - Build scripts
- Start scripts
- Development-only scripts
- Dependencies and development dependencies
A project may declare its expected runtime like this:
{
"engines": {
"node": "22.x"
},
"packageManager": "npm@11.4.2"
}Use versions supported by your chosen deployment provider. Do not copy the example versions without checking your local project and the provider’s current documentation.
Be careful with overly broad declarations such as:
{
"engines": {
"node": ">=18"
}
}That range may allow a future major version that the application has never been tested against.
Record the selected version in the README or deployment notes so that another developer can reproduce the same environment.
Step 4: Make Dependency Installation Reproducible
A dependency lockfile records the exact dependency tree selected for the project.
For npm, that file is normally package-lock.json. Other package managers use files such as:
yarn.lock
pnpm-lock.yaml
bun.lockCommit the lockfile that belongs to the package manager you actually use. Avoid keeping multiple package-manager lockfiles unless the project intentionally supports several workflows.
For npm projects, a deployment or continuous-integration environment should generally use:
npm ciUnlike a normal install that may update dependency resolution, npm ci requires the lockfile and package.json to agree. It exits with an error rather than silently updating an inconsistent lockfile. npm documents it as intended for automated environments such as tests, continuous integration, and deployment.
Test the clean installation locally.
First, make sure uncommitted work is safe. Then remove the installed dependency directory:
rm -rf node_modules
npm ciOn PowerShell:
Remove-Item node_modules -Recurse -Force
npm ciRemoving node_modules is destructive to the local installed dependency directory, although it does not remove declared dependencies from the repository. Confirm that you are in the correct project directory before running the command.
After installation, run the project’s checks again.
If npm ci fails because package.json and package-lock.json disagree, do not edit the lockfile manually. Review why they diverged. A developer or AI assistant may have changed one without correctly updating the other.
Step 5: Identify the Real Build and Start Commands
Deployment platforms usually need to know how to install, build, and run the project.
For the sample application, the scripts might resemble:
{
"scripts": {
"dev": "concurrently \"npm:dev:client\" \"npm:dev:server\"",
"dev:client": "vite",
"dev:server": "nodemon server/index.js",
"build": "vite build",
"start": "node server/index.js",
"test": "vitest run"
}
}The development command is not normally the production start command.
Development tools may:
- Watch files for changes
- Restart processes automatically
- Display detailed errors
- Use a local proxy
- Serve unoptimized assets
- Enable hot-module replacement
- Depend on development-only packages
Test the production build directly:
npm ci
npm run buildThen inspect the generated output.
For a Vite frontend, that may be:
dist/Do not assume the build succeeded because the final line was green. Scroll through the complete output and review warnings about missing variables, large bundles, incompatible packages, or generated routes.
If the frontend and backend are separate services, document separate commands:
Frontend install: npm ci
Frontend build: npm run build
Frontend output: dist
Backend install: npm ci
Backend start: npm start
Backend health path: /healthThe hosting provider should not need to guess these values.
Step 6: Remove Localhost Assumptions
Hardcoded local addresses are among the most common causes of failed first deployments.
Search the repository for values such as:
localhost
127.0.0.1
http://
3000
5173Useful commands include:
grep -R "localhost" src server --exclude-dir=node_modules
grep -R "127.0.0.1" src server --exclude-dir=node_modulesOn PowerShell:
Get-ChildItem src,server -Recurse -File |
Select-String "localhost|127\.0\.0\.1"Not every match is wrong. Test configuration and documentation may intentionally use localhost. Production code should obtain environment-specific service URLs from configuration.
Instead of:
const apiUrl = "http://localhost:3000";use a documented configuration value:
const apiUrl = import.meta.env.VITE_API_URL;
if (!apiUrl) {
throw new Error("VITE_API_URL is required");
}Remember that variables included in frontend code are normally visible to the browser. A variable such as VITE_API_URL can contain a public API base URL, but it must not contain a secret API credential.
On the backend, use server-only configuration:
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error("DATABASE_URL is required");
}Failing early with a clear configuration error is safer than allowing the application to start in a partially broken state.
Step 7: Create an Environment-Variable Inventory
Do not wait until the hosting dashboard asks for variables.
Search the codebase for configuration access:
grep -R "process.env" . \
--exclude-dir=node_modules \
--exclude-dir=distFor a Vite frontend:
grep -R "import.meta.env" srcRecord each variable in a table or deployment document:
| Variable | Used by | Sensitive | Example purpose |
|---|---|---|---|
DATABASE_URL | Backend | Yes | PostgreSQL connection |
SESSION_SECRET | Backend | Yes | Session protection |
EMAIL_API_KEY | Worker | Yes | Email provider access |
APP_BASE_URL | Backend | No | Public application URL |
VITE_API_URL | Frontend | No | Public API base URL |
LOG_LEVEL | Backend | No | Logging verbosity |
Create a safe .env.example:
DATABASE_URL=postgresql://USER:PASSWORD@HOST:5432/DATABASE
SESSION_SECRET=replace-with-a-long-random-value
EMAIL_API_KEY=replace-with-provider-key
APP_BASE_URL=https://example.com
VITE_API_URL=https://api.example.com
LOG_LEVEL=infoThese are placeholders, not usable credentials.
The Twelve-Factor App methodology recommends keeping deployment-specific configuration outside source code so values can change between deployments without code changes. It also notes that configuration files can be committed accidentally or scattered across a project.
Separate ordinary variables from secrets. GitHub notes that normal workflow variables are not masked in build output, while sensitive values should use secret storage instead.
The later articles in this series will cover environment variables and API keys in greater depth.
Step 8: Search for Exposed Secrets
Before connecting the repository to a deployment service, search for credentials that may already be present.
Review:
- Source files
- Environment files
- Configuration files
- Test fixtures
- CI workflow files
- Build logs
- README examples
- Git history
Search terms might include:
API_KEY
SECRET
TOKEN
PASSWORD
PRIVATE_KEY
DATABASE_URL
Authorization
BearerA simple search can find obvious cases:
git grep -n -E 'API_KEY|SECRET|TOKEN|PASSWORD|PRIVATE_KEY'This command also finds legitimate variable names, so inspect the values rather than deleting every match.
A secret scanner can provide another layer of protection, but it does not replace manual review. AI-generated credentials, unusual formats, and secrets embedded inside larger strings may not match known patterns.
When a real credential has been committed:
- Disable or rotate the credential through the provider.
- Replace it with a new credential stored outside the repository.
- Review where the exposed credential could have been used.
- Consider whether Git history must be cleaned.
- Inform affected collaborators when appropriate.
Deleting the current line is not enough because earlier commits may still contain the value.
GitHub supports repository-, organization-, and environment-scoped secrets. Environment-level secrets can separate staging credentials from production credentials, and deployment protection rules can restrict access before a workflow reaches an environment.
Step 9: Make the Server Listen Correctly
A local Express server often uses a fixed port:
app.listen(3000);A cloud platform normally provides the port through an environment variable. The server should also listen on an interface reachable by the platform.
An illustrative configuration is:
const port = Number(process.env.PORT || 3000);
const host = process.env.HOST || "0.0.0.0";
const server = app.listen(port, host, () => {
console.log(`Server started on ${host}:${port}`);
});Do not expose internal configuration or secrets in the startup log.
Also add graceful shutdown behavior where the framework and database client support it:
async function shutdown(signal) {
console.log(`${signal} received; shutting down`);
server.close(async () => {
try {
await database.close();
process.exit(0);
} catch (error) {
console.error("Shutdown failed", error);
process.exit(1);
}
});
}
process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT"));This code is illustrative. Your database library may use a different close method, and production code should prevent shutdown from hanging indefinitely.
Cloud services restart processes during deployments, scaling, maintenance, and failure recovery. Applications should start predictably and stop without abandoning important work. The Twelve-Factor App guidance describes fast startup and graceful shutdown as important for reliable deployment and scaling behavior.
Step 10: Add a Health-Check Endpoint
A health endpoint gives the platform or monitoring system a small, predictable request that indicates whether the process can serve traffic.
A basic Express endpoint might be:
app.get("/health", (req, res) => {
res.status(200).json({
status: "ok"
});
});Do not include:
- Environment variables
- Database connection strings
- Dependency versions that create unnecessary exposure
- Internal hostnames
- Stack traces
- User information
A deeper readiness check may verify that critical dependencies are available:
app.get("/ready", async (req, res) => {
try {
await database.query("SELECT 1");
res.status(200).json({ status: "ready" });
} catch {
res.status(503).json({ status: "unavailable" });
}
});Use dependency checks carefully. A slow or expensive query can create additional load if the hosting provider calls the endpoint frequently.
You may separate:
- Liveness: Is the process running?
- Readiness: Can it currently handle requests?
For a first deployment, a small health endpoint is often enough to help detect startup and routing problems.
Step 11: Review Production Error Handling
Development errors should help developers. Production errors should help users without exposing internal details.
Avoid responses like:
res.status(500).json({
error: error.stack,
databaseUrl: process.env.DATABASE_URL
});Use a generic public response and record useful details in protected logs:
app.use((error, req, res, next) => {
console.error("Unhandled request error", {
method: req.method,
path: req.path,
message: error.message
});
res.status(500).json({
error: "The request could not be completed."
});
});This example still requires improvement for structured logging and sensitive-data control. Even error.message may contain confidential data depending on the library.
Review whether logs contain:
- Passwords
- Authentication tokens
- Cookies
- Authorization headers
- Full database connection strings
- Payment information
- Full request bodies
- Unnecessary personal information
OWASP’s deployment guidance recommends reviewing configuration and ensuring that logs do not store secrets or unnecessary sensitive information.
Step 12: Verify Server-Side Input Validation
HTML attributes and frontend checks improve usability, but they do not protect the backend from direct requests.
For an appointment request, the server should validate:
- Required fields
- String length
- Date format
- Allowed appointment range
- Email format
- Supported status values
- Whether the selected slot exists
- Whether the slot is still available
An illustrative validation flow is:
app.post("/api/appointments", async (req, res) => {
const { name, email, slotId } = req.body;
if (
typeof name !== "string" ||
name.trim().length < 1 ||
name.length > 100
) {
return res.status(400).json({ error: "Invalid name." });
}
if (
typeof email !== "string" ||
email.length > 254
) {
return res.status(400).json({ error: "Invalid email." });
}
if (
typeof slotId !== "string" ||
slotId.length > 100
) {
return res.status(400).json({ error: "Invalid appointment slot." });
}
// Continue with authorization, availability checks,
// and a transaction or database constraint.
});This is not a complete email validator or booking implementation. It demonstrates that the server should reject malformed data before database work.
OWASP recommends validating input on a trusted system and favors allowlisting expected formats and ranges for structured data.
Step 13: Check Authentication and Authorization Separately
A successful login does not prove that protected actions are safe.
Review every sensitive server endpoint:
GET /api/admin/appointments
PATCH /api/appointments/:id
DELETE /api/appointments/:id
POST /api/admin/availabilityFor each route, answer:
- Must the requester be logged in?
- Which role or ownership rule applies?
- Is authorization checked on the server?
- Can the requester change the resource ID?
- Does the database query include the ownership condition?
- What response is returned when access is denied?
Do not rely on hiding an admin link in React. A user can send a request directly to the backend.
Also confirm that test users, bypass headers, development login routes, and hardcoded administrator IDs are removed or securely disabled before deployment.
Step 14: Prepare Database Migrations
A local database may contain tables created through an ORM command, a graphical tool, or manual SQL. Production needs a repeatable schema history.
Confirm that the repository includes migration files representing the current schema.
Depending on the framework, commands may resemble:
npm run migrateor:
npx prisma migrate deployor:
npx knex migrate:latestDo not use these commands against a real production database based only on these examples. Confirm the tool, environment, target database, backup state, and migration contents first.
Before first deployment:
- Create a new empty local or test database.
- Apply all migrations from the beginning.
- Confirm that the schema is created successfully.
- Run seed data only when it is safe and intended.
- Start the application against the new database.
- Complete the critical user workflow.
- Review how migrations will run during deployment.
- Document how a failed migration will be handled.
Avoid automatically running risky schema changes every time the application process starts. Multiple instances may start simultaneously, and a failed migration can prevent all of them from becoming available.
Database migrations receive a dedicated article later in this series.
Step 15: Remove Local File Persistence Assumptions
Search for code that writes to the project filesystem:
fs.writeFile
fs.writeFileSync
fs.createWriteStream
multer
uploads/
tmp/
data.json
sqliteLocal writes may not persist after deployment. Multiple instances may also have separate filesystems.
Classify every write:
- Temporary processing file
- User-uploaded durable file
- Application data
- Cache
- Log
- Database file
- Generated export
Temporary files may use a platform-supported temporary directory and should be deleted after use.
Durable uploads should generally go to object storage or another explicitly persistent service.
Application records should normally go to a production database.
Logs should go to standard output or a supported logging system rather than an unmanaged local logfile. The Twelve-Factor App approach treats logs as event streams that the execution environment can collect and route.
Step 16: Test the Production Build in a Clean State
A clean test exposes hidden local dependencies.
The ideal sequence is:
git status
npm ci
npm run build
npm test
npm startYour actual commands may differ.
For a stronger check, clone the repository into a new directory or use a temporary container. Then supply only the variables documented in .env.example.
Verify:
- Installation works without an existing
node_modules - The build does not depend on untracked files
- The application starts with the documented command
- Required variables produce clear startup errors when missing
- The frontend calls the configured API
- Static assets load correctly
- Protected routes remain protected
- The main database workflow succeeds
- Logs contain useful but non-sensitive information
Do not claim that the application passed this test unless you personally ran the commands and reviewed the results.
Step 17: Run Dependency and Code Review Checks
Review the packages introduced by AI-assisted development.
For npm:
npm outdated
npm auditInterpret the results carefully. A reported vulnerability does not automatically mean the application is exploitable, and a clean report does not prove the application is secure.
Review:
- Whether each dependency is still used
- Whether a built-in API could replace it
- Whether it runs in the browser or server
- Whether it has install scripts
- Whether the selected version is maintained
- Whether upgrading introduces breaking changes
- Whether the package was added only to solve a small task
Do not run an automatic force upgrade against an important project without reviewing the proposed dependency changes and testing the result. Forced upgrades can introduce major-version changes or break APIs.
Also review the Git diff manually:
git diff --stat
git diffFocus on deployment-related changes such as:
- Runtime declarations
- Build scripts
- Start commands
- Environment-variable names
- CORS configuration
- Cookie settings
- Database connection setup
- Error handling
- Logging
- File storage
- New dependencies
Step 18: Document the Deployment Contract
Create a short document that describes what the platform must provide.
For the sample app:
Runtime:
- Node.js version supported by the project and provider
Frontend:
- Install: npm ci
- Build: npm run build
- Output: dist
- Public variable: VITE_API_URL
Backend:
- Install: npm ci
- Start: npm start
- Port: supplied through PORT
- Health check: /health
Database:
- PostgreSQL
- Connection supplied through DATABASE_URL
- Migrations run as a controlled release step
Worker:
- Start: npm run worker
- Requires DATABASE_URL and EMAIL_API_KEY
Reminder:
- Command: npm run send-reminders
- Schedule: hourly
- Must prevent duplicate reminders
Secrets:
- DATABASE_URL
- SESSION_SECRET
- EMAIL_API_KEY
Non-secret configuration:
- APP_BASE_URL
- LOG_LEVELThis becomes the bridge between your repository and the hosting dashboard.
It also gives an AI assistant clear constraints. Instead of asking, “Deploy my app,” you can ask it to compare the repository against this deployment contract without modifying unrelated files.
Step 19: Define Post-Deployment Verification
Write the verification plan before deploying.
For the appointment application:
- Open the public frontend URL.
- Confirm that JavaScript, CSS, and images load.
- Open the backend health endpoint.
- Create a test appointment.
- Confirm that the record appears in the production test database.
- Confirm that duplicate booking is rejected.
- Confirm that an unauthorized user cannot access administrative data.
- Trigger a controlled validation error.
- Confirm that the user receives a safe message.
- Confirm that protected logs contain enough context.
- Verify the email workflow using a designated test recipient.
- Confirm the scheduled task is configured but cannot create duplicate actions.
- Review deployment and runtime logs.
- Remove or clearly label test data.
Do not use real customer data for an initial deployment test.
A successful home page is not sufficient verification for a full-stack application.
Step 20: Prepare a Rollback Starting Point
Before deployment, record:
Known-good commit:
Deployment branch:
Database backup or empty database state:
Migration version:
Required environment variables:
Previous service configuration:
Verification owner:
Rollback trigger:For a first deployment with no real users, rollback may mean restoring the previous deployment or disabling the public URL while you fix the issue.
Once production data exists, rollback becomes more complicated. Restoring old application code does not automatically undo a database migration, reverse an email, or delete an external API action.
Do not design the first migration or deployment in a way that destroys the only copy of important data.
The later articles in this series will cover database backups, live updates, and rollback procedures individually.
Common First-Deployment Preparation Mistakes
Preparing the platform before preparing the repository
A hosting dashboard cannot correct undocumented commands, hardcoded URLs, or missing migrations.
Make the repository reproducible first.
Using the development command in production
Commands using Vite’s development server, nodemon, hot reloading, or framework debug modes are not normal production start commands.
Define and test a production command.
Committing .env.production
A production filename does not make a credential safe to commit.
Store production values in the platform’s secret-management system.
Assuming an environment variable is private
Frontend build variables are often compiled into browser assets.
Only place public configuration in frontend variables.
Allowing the AI assistant to change multiple layers at once
A broad deployment prompt may modify framework configuration, database code, authentication, and dependencies in one edit.
Request a proposed file list and make focused changes.
Running migrations automatically without review
A build success does not prove a migration preserves existing data.
Inspect generated SQL or ORM operations and test them against disposable data first.
Treating logs as a place for complete request data
Complete request logging can expose credentials, cookies, personal information, and user content.
Log the minimum context needed for operations.
Testing only with your existing local database
Your local database may contain manually created tables or old schema state.
Apply migrations to a new empty database and test again.
AI Prompt for a Deployment-Preparation Review
Goal:
Review this repository for first-deployment readiness without deploying it.
Project context:
- Frontend:
- Backend:
- Runtime:
- Package manager:
- Database:
- Background workers:
- Scheduled tasks:
- File uploads:
- Target platform:
Constraints:
- Do not modify files until you provide a review.
- Do not add dependencies without explaining why.
- Do not replace the framework.
- Do not expose server secrets to frontend code.
- Do not delete or reset any database.
- Do not run migrations against production.
- Treat all examples as untested until commands are executed.
- Use current official framework and platform documentation.
Review:
1. Required runtime and package-manager versions
2. Install, build, test, and start commands
3. Missing or inconsistent lockfiles
4. Hardcoded localhost URLs and ports
5. Required environment variables
6. Possible secrets in source or configuration
7. Development-only settings
8. Server-side validation and authorization risks
9. Local filesystem persistence assumptions
10. Database migration readiness
11. Logging and error exposure
12. Health-check readiness
13. Recommended focused changes
14. Commands to verify each change
Expected output:
- Findings ranked by deployment risk
- Exact affected files
- Proposed changes without applying them
- Verification command for every proposed change
- Items that require manual security reviewUse the result as a review aid. Confirm every finding against the repository and current official documentation before applying changes.
First-Deployment Preparation Checklist
- The current working version is committed to Git.
- Deployment preparation is happening on a focused branch.
- Untracked files have been reviewed before staging.
- Local environment files are excluded from Git.
- Previously committed secrets have been rotated where necessary.
- The required runtime version is documented.
- The selected package manager is clear.
- Only the correct dependency lockfile is committed.
- A clean dependency installation succeeds.
- The production build command succeeds.
- The production start command is documented.
- Development-only commands are not used as production commands.
- Hardcoded localhost URLs have been reviewed.
- The backend uses the platform-provided port.
- Required environment variables are inventoried.
- Sensitive and non-sensitive variables are distinguished.
.env.examplecontains placeholders rather than credentials.- Frontend variables contain no secrets.
- The repository has been reviewed for exposed credentials.
- The server fails clearly when mandatory configuration is missing.
- A safe health-check endpoint exists when appropriate.
- Production errors do not expose stack traces or configuration.
- Logs exclude secrets and unnecessary personal information.
- Important input is validated on the server.
- Sensitive routes enforce authorization on the server.
- Test accounts and authentication bypasses have been removed.
- Database migrations can build a new database from an empty state.
- Risky migrations are not run automatically without review.
- Local filesystem writes have been classified.
- Durable files use appropriate persistent storage.
- The application has been tested from a clean installation.
- Dependencies have been reviewed for necessity and risk.
- The final Git diff has been reviewed manually.
- The deployment contract is documented.
- Post-deployment verification steps are written.
- A known-good commit and rollback starting point are recorded.
- No real user data is required for the initial deployment test.
