KAHIBARO
Discord Login Register

23.1. Development vs Production

Why Environments Matter

When you build backend applications, you rarely run the same setup everywhere. You will usually have at least:

They serve different purposes, follow different rules, and are configured differently. Understanding this difference is critical before you deploy anything.

Key idea: Never treat your development environment as “almost production.”
Production must be safer, stricter, and more controlled than development.

In this chapter we focus on the mindset and practical differences between development and production, not on the details of any specific tool.

What Is a Development Environment?

A development environment is where you and your team write, run, and debug code. Its primary goal is to help you move fast, experiment, and find bugs early.

Typical examples of development environments:

Characteristics of Development Environments

Development environments are usually:

Here are some common properties:

AspectDevelopment behavior
Code reloadingAutomatic reload on file changes
Error visibilityDetailed error pages with stack traces
LoggingVery verbose, includes debug messages and sensitive details
SecurityRelaxed, often using dummy secrets or test certificates
DatabaseCan be reset or dropped often, test data is allowed
DependenciesCan change frequently; breaking changes are acceptable
PerformanceNot a priority; correctness and visibility matter more

Example: Python / FastAPI dev settings

In development you might:

bash
uvicorn app.main:app --reload
env
ENV=development
DEBUG=true
SECRET_KEY=dev-secret-key-not-for-production
DATABASE_URL=postgresql://devuser:devpass@localhost/dev_db
python
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # fine in dev, not in production
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

This is comfortable in development, but it would be dangerous in production.

What Is a Production Environment?

A production environment is where your real users interact with your application. Any issue here can cost money, data, or trust.

Typical production environments:

Characteristics of Production Environments

Production environments are usually:

Here are common production properties:

AspectProduction behavior
Code reloadingDisabled. New code only after a deployment process
Error visibilityNo detailed stack traces to users
LoggingStructured, safe, and centralized
SecurityStrict, with real secrets and TLS certificates
DatabaseContains real data, must never be dropped or modified casually
DependenciesCarefully controlled and versioned
PerformanceImportant, often tuned and tested

Example: Python / FastAPI prod settings

In production you might:

bash
gunicorn -k uvicorn.workers.UvicornWorker app.main:app -w 4 -b 0.0.0.0:8000
env
ENV=production
DEBUG=false
SECRET_KEY=super-long-random-secret-from-secret-manager
DATABASE_URL=postgresql://appuser:longpassword@db-prod/app_db
python
allowed_origins = [
    "https://myapp.com",
    "https://admin.myapp.com",
]
app.add_middleware(
    CORSMiddleware,
    allow_origins=allowed_origins,
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)

Key Differences Between Development and Production

Both environments run the same application, but with different configuration, infrastructure, and rules.

Configuration and Environment Variables

You usually use environment variables to switch behavior.

Common example:

python
import os
ENV = os.getenv("ENV", "development")
DEBUG = ENV == "development"

You might then use this in your code:

python
if DEBUG:
    print("Running in development mode")

In production, ENV will be "production", so you avoid debug behaviors.

Rule: Configuration lives outside the code, usually in environment variables.
Never hard-code production secrets in your source code.

Typical variables that differ:

VariableDevelopment exampleProduction example
ENVdevelopmentproduction
DEBUGtruefalse
DATABASE_URLLocal DB or Docker DBManaged database service (for example, AWS RDS)
SECRET_KEYSimple, short keyLong, random, stored in a secret manager
ALLOWED_HOSTS* or localhostSpecific domains for your app
EMAIL_BACKENDConsole or sandbox providerReal email provider (for example, SendGrid)

Error Handling and Debugging

In development:

In production:

Example of different behavior:

python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import os
app = FastAPI()
ENV = os.getenv("ENV", "development")
@app.exception_handler(Exception)
async def generic_exception_handler(request: Request, exc: Exception):
    if ENV == "development":
        # Show detailed info, only safe in development
        return JSONResponse(
            status_code=500,
            content={"error": str(exc), "type": str(type(exc))},
        )
    else:
        # Generic message only, safe for production
        return JSONResponse(
            status_code=500,
            content={"error": "Internal server error"},
        )

Security Settings

Security in development is often simplified. In production, security rules must be much stricter.

Examples:

Rule: It is acceptable to relax security only in environments that have no real users and no real data.

Databases and Data

One of the biggest differences is how you treat data.

A typical pattern:

Logging and Monitoring

In development:

In production:

Example of changing log level:

python
import logging
import os
ENV = os.getenv("ENV", "development")
log_level = logging.DEBUG if ENV == "development" else logging.INFO
logging.basicConfig(level=log_level)
logger = logging.getLogger(__name__)
logger.debug("Debug message")  # Only appears in development
logger.info("Info message")    # Appears in all environments

Performance and Scaling

Development:

Production:

Even if your application is small, production should be designed so that it can scale when needed.

Infrastructure and Deployment

In development you often start services manually:

bash
uvicorn app.main:app --reload
docker compose up

In production:

You will learn the details in other chapters about Docker and CI/CD, but the key idea is that production deployments are repeatable, automated, and controlled.

Example: Same App, Different Environments

Assume you have this FastAPI application:

python
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health_check():
    return {"status": "ok"}

You could run it in two different environments with different Docker Compose files.

Development docker-compose.yml

yaml
version: "3.9"
services:
  api:
    build: .
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
    ports:
      - "8000:8000"
    environment:
      - ENV=development
      - DEBUG=true
      - DATABASE_URL=postgresql://devuser:devpass@db/dev_db
    volumes:
      - .:/code
  db:
    image: postgres:16
    environment:
      - POSTGRES_USER=devuser
      - POSTGRES_PASSWORD=devpass
      - POSTGRES_DB=dev_db
    ports:
      - "5432:5432"

Characteristics:

Production docker-compose.yml

yaml
version: "3.9"
services:
  api:
    image: myregistry/myapp:1.0.0
    command: gunicorn -k uvicorn.workers.UvicornWorker app.main:app -w 4 -b 0.0.0.0:8000
    ports:
      - "8000:8000"
    environment:
      - ENV=production
      - DEBUG=false
      - DATABASE_URL=${DATABASE_URL}
      - SECRET_KEY=${SECRET_KEY}
    restart: always

Characteristics:

Testing and Staging Environments (Context)

Besides development and production, teams often use test or staging environments.

Common pattern:

EnvironmentPurposeData type
DevDeveloper work, experimentsFake / temporary
TestAutomated testsControlled test
StagingPre-production tests, close to productionFake but realistic
ProdReal users and workloadReal

These environments behave more like production as you move from dev to prod. Staging is often almost identical to production infrastructure, but with fake data.

You do not need all these for small projects, but you should always have at least development and production.

Typical Pitfalls When Moving from Development to Production

Beginners often run into similar problems when they first deploy an app.

Forgetting to disable debug mode

For example, in Flask:

python
app.run(debug=True)

If this is used in production, it can expose sensitive info.

In FastAPI, running with --reload in production has a similar risk and performance cost.

Rule: Never run your application in debug or reload mode in production.

Using the same database for dev and prod

If you accidentally point your development environment at the production database, you might:

Always keep separate databases and verify connection strings.

Committing secrets to source control

If your .env contains production secrets and you commit it, you risk:

Use .gitignore and a secure method for storing secrets, such as a secret manager or environment variables configured on the server.

Mental Model: Same Code, Different Configuration

You should aim for this:

Mathematically you can think of it as:

$$
\text{Behavior} = f(\text{Code}, \text{Configuration})
$$

Where:

In good backend design:

Principle: Code does not change between environments, only configuration does.

You build your app once, then run it with different configuration for development, testing, and production.

Summary

This foundation will help you in the next chapters, where you prepare your application specifically for production and learn to deploy it safely.

Views: 6

Comments

Please login to add a comment.

Don't have an account? Register now!