You’ve inherited a growing monolith database. Names collide, and accidental cross-feature queries cause bugs. You need a clean way to group objects per feature/team and create tables with safe defaults. PostgreSQL schemas give you namespaces, and well-structured tables make intent clear from day one.
public.auth.users).
Use schemas early to avoid name collisions and to manage
permissions.user is reserved. Prefer app_user or
auth.user. Avoid quoting identifiers unless necessary.Follow these steps to organize with schemas and create solid tables.
Create an accounting schema and a table inside it:
CREATE SCHEMA accounting;
CREATE TABLE accounting.invoices (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
customer_id INT NOT NULL,
amount NUMERIC(10,2) NOT NULL,
issued_at TIMESTAMPTZ DEFAULT now()
);Outcome: a new namespace and a table with clear types and a primary key.
Two departments track their own employee lists without conflicts:
CREATE SCHEMA sales;
CREATE SCHEMA hr;
CREATE TABLE sales.employees (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE hr.employees (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL
);Outcome: same table name, different schemas, no conflict.
Move tables out of public with minimal risk:
ALTER TABLE public.some_table SET SCHEMA app;schema.table (or adjust
search_path).public if you no longer want new objects
there.Grant usage on a schema to a role and limit writes as needed:
GRANT USAGE ON SCHEMA accounting TO app_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA accounting TO app_role;public: OK for prototypes or learning. As
concerns split (auth, billing, analytics), add schemas.public leads to all-or-nothing
permissions and name collisions.user forces quoting and
causes tool friction.public schema.search_path to test resolution.audit.*. Verify
that a write fails.ALTER TABLE ... SET SCHEMA ... RENAME TO ...).hr.employees and sales.employees,
then write a query that unions them with a source column.public so no new objects can be created
there. Record the GRANT statements you changed.