My Problem đ€
In many projects Iâve seen how application code ends up doing things it shouldnât: creating roles, configuring database server parameters, managing instance-level permissions. Everything mixed in the same place, as if it were a single responsibility.
But itâs not. The database has two distinct worlds: administration and development.
Mixing them is one of the most common mistakes, and also one of the most expensive. A developer configuring the instance from their code is crossing a boundary that doesnât belong to them. And an administrator trying to decide which tables or schemas the application uses is doing the same from the other side.
My Solution đ§©
The separation is conceptually clean: administration belongs to the server, development belongs to the code.
The administratorâs territory
The administrator manages the database instance. Their work includes:
- Server configuration â performance parameters, instance-level extensions, settings that require a restart
- Roles and credentials â creating users, assigning secure passwords, defining who can connect
- Databases â creating them, setting timeouts, revoking default access
All of this is infrastructure. These are decisions made once, executed by someone with superuser privileges, and they have nothing to do with business logic. Ideally, they live as versioned SQL scripts in the repository, but separate from the application code.
The developerâs territory
The developer manages the applicationâs data structure: schemas, tables, indexes, views, and everything that needs to exist for the application to work.
This responsibility is expressed through migrations â versioned, reversible, and reviewable pieces of code like any other change. It doesnât matter if you use Swift, Python, Go, or TypeScript: the principle is the same. Your applicationâs data structure must be automatically reproducible in any environment.
The gray area: admin or development?
There are cases that seem ambiguous. Database roles are a good example. The role itself â with its password and connection permissions â is an infrastructure concept. The administrator creates it.
But permissions on specific tables â what it can read, what it can write, on which schema â that depends on the developer. The table has to exist before it can have permissions, so those permissions go in migrations, not in infrastructure scripts.
The rule I apply is simple: if the object exists before the application, itâs administration. If its existence depends on the application creating it, itâs development.
My Result đŻ
When you apply this separation, the setup process becomes clear and reproducible:
- The administrator prepares the instance â server configured, roles created, database ready
- The developer runs the migrations â schemas, tables, and application permissions applied automatically
Each layer has its own tool. The administrator doesnât touch the application code. The developer doesnât need superuser credentials. Nobody steps on the otherâs territory.
This separation also makes teamwork easier: the administration scripts are run by whoever has server access, just once. The migrations are run by any developer on the team in their local environment, as many times as needed.
The database is not yours alone as a developer. But the data structure of your application is.
Keep coding, keep running đââïž