Communitygithub.com

Convenções de Nomenclatura e Esquemas PostgreSQL

Convenções de nomenclatura e esquemas PostgreSQL (tabelas, colunas, chaves, restrições, índices, relações, tipos) para .NET/EF Core, Node.js (Drizzle, Prisma, TypeORM, Knex) e Go (sqlc). Use ao projetar ou revisar esquemas, escrever migrações, configurações de entidades, consultas sqlc ou SQL, ou quando perguntar como nomear objetos de banco de dados. Relata desvios ao usuário em vez de alterá-los silenciosamente.

O que é Convenções de Nomenclatura e Esquemas PostgreSQL?

Este skill fornece convenções abrangentes de nomenclatura e design de esquemas PostgreSQL para ambientes de desenvolvimento modernos, incluindo .NET/EF Core, Node.js (Drizzle, Prisma, TypeORM, Knex) e Go (sqlc). Ele cobre desde regras gerais, como usar snake_case minúsculo sem identificadores entre aspas, até padrões específicos para colunas padrão (como `created_at` e `updated_at`), chaves e restrições (como `pk_orders` e `fk_orders_customers_customer_id`), e relações (um-para-muitos, muitos-para-muitos, auto-referência). O skill opera em dois modos: Modo A, onde o usuário permite que o assistente aplique as convenções diretamente ao criar nomes; e Modo B, onde nomes existentes ou ditados devem ser relatados e aprovados antes de qualquer alteração. Ele também fornece configurações específicas para ORMs ativarem snake_case automaticamente e práticas recomendadas para Go + sqlc, como apontar o esquema para o diretório de migrações e usar `sql_package: pgx/v5`. O foco principal é evitar bugs comuns relacionados a identificadores entre aspas, tipos de dados incorretos (como `json` em vez de `jsonb`) e nomes de objetos que podem causar problemas de consulta ou ferramentas.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/EgoDZB/claude-skills/tree/HEAD/skills/postgres-conventions

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Convenções PostgreSQL

Como aplicar

Existem dois modos. Decida qual se aplica antes de escrever qualquer coisa.

Modo A — você escolhe os nomes. O usuário pediu um esquema, entidade ou migração e não ditou nomes. Aplique as convenções abaixo diretamente.

Modo B — os nomes já existem ou foram ditados. O usuário especificou nomes, ou código existente, migrações ou o banco de dados contêm nomes que desviam dessas convenções. Nunca renomeie silenciosamente. Relate e pergunte.

Formato de relatório, no idioma do usuário, antes de fazer alterações:

Nomenclatura:
Deve corrigir
- `"UserProfile"` (tabela) → `user_profiles` — PascalCase entre aspas deve ser citado em toda consulta para sempre
- `created` timestamp DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC') → `created_at timestamptz DEFAULT now()` — o valor muda quando o TimeZone da sessão não é UTC
Recomendado
- `users.user_email` → `users.email` — o nome da tabela já é o contexto

Então faça uma pergunta: aplicar tudo, aplicar apenas "Deve corrigir", ou manter como está. Aguarde a resposta.

Regras para relatar:

  • Deve corrigir = causa bugs, quebra consultas ou ferramentas: identificadores de maiúsculas/minúsculas mistas entre aspas, palavras reservadas, nomes com mais de 63 bytes, timestamp sem fuso horário, defaults de tempo errados, json/float/money para dados que precisam de jsonb/ numeric, índice FK ausente, locks fora de uma transação.
  • Recomendado = estilo e consistência.
  • Relate apenas objetos tocados pela tarefa atual. Se você notou outros desvios em outro lugar, adicione uma linha: "Também N desvios semelhantes em — listá-los?"
  • Objetos em migrações aplicadas ou em um banco de dados ativo: uma renomeação é uma mudança de quebra. Apenas sugira; se aprovado, implemente como uma nova migração. Nunca edite uma migração aplicada.
  • Se o codebase usa consistentemente uma convenção diferente, ela vence. Mencione a diferença uma vez, então siga a convenção do projeto sem sinalizar cada ocorrência.
  • Nomes produzidos pela convenção de nomenclatura do ORM (por exemplo, EFCore.NamingConventions) são aceitos como estão.
  • Uma vez que o usuário confirme um nome não conforme, use-o e não o levante novamente nesta sessão.

Regras gerais

  • snake_case, minúsculo, nunca identificadores entre aspas.
  • Máximo de 63 bytes por identificador; nomes mais longos são truncados silenciosamente. Encurte de forma significativa, não cortando.
  • Tabelas no plural: users, orders, order_items.
  • Colunas no singular, sem prefixo de tabela: users.email, não users.user_email.
  • Palavras completas, exceto abreviações comuns: id, url, ip, sku.
  • Nunca use palavras reservadas ou ambíguas como nomes: user, order, group, table, limit, offset, desc, end, date, time, timestamp.
  • Sem prefixos de tipo ou objeto: sem tbl_users, str_name.
  • Booleanos são positivos: is_deleted, nunca is_not_deleted.

Colunas padrão

PropósitoNomeTipo
Chave primáriaidbigint generated always as identity ou uuid v7
Criado / atualizadocreated_at, updated_attimestamptz not null default now()
Exclusão lógicadeleted_attimestamptz null
Auditoriacreated_by, updated_byFK para users.id
Flagsis_active, is_verified, has_children, can_editboolean not null default false
Statusstatustext + check, enum, ou tabela de consulta
Apenas databirth_date, due_ondate
Validadeexpires_at, valid_from, valid_totimestamptz
Dinheiroamount, unit_price, total_amount + currency_codenumeric(19,4) + char(3), ou amount_minor bigint
Contadoresitems_count, retry_countinteger / bigint
Ordenaçãosort_orderinteger
Chave de negóciocode, slug, external_idtext + único
Locatáriotenant_idFK
Concorrênciaversion ou sistema xmininteger / xid
Dados livresmetadata, payload, settingsjsonb

Sufixos carregam significado: _at instante, _on/_date data de calendário, _id referência, _count contador, _url, _path, _hash.

Chaves e restrições

ObjetoPadrãoExemplo
Chave primáriapk_<tabela>pk_orders
Chave estrangeirafk_<tabela>_<tabela_ref>_<coluna>fk_orders_customers_customer_id
Restrição únicauq_<tabela>_<cols>uq_users_email
Chave alternativa (EF Core)ak_<tabela>_<cols>ak_products_sku
Checkck_<tabela>_<regra>ck_orders_amount_positive
Exclusãoex_<tabela>_<cols>ex_bookings_room_id_period
Índiceix_<tabela>_<cols>ix_orders_customer_id_created_at
Índice únicoux_<tabela>_<cols>ux_users_email
Índice parcialix_<tabela>_<cols>_<condição>ix_users_email_not_deleted
Índice por métodoix_<tabela>_<col>_<método>ix_products_tags_gin
Gatilhotrg_<tabela>_<ação>trg_orders_set_updated_at
Função de gatilhoverbo + objetoset_updated_at()
SequênciaPadrão PostgreSQLorders_id_seq
  • Coluna FK: <tabela referenciada, singular>_id, ou um nome de papel quando o papel importa: author_id, sender_id, recipient_id, parent_id.
  • O padrão FK inclui a coluna, então várias FKs para uma tabela permanecem únicas: fk_messages_users_sender_id, fk_messages_users_recipient_id.
  • PostgreSQL não indexa colunas FK. Toda FK precisa de um índice, a menos que um índice composto já comece com ela. EF Core cria esses por convenção; Drizzle, Prisma, TypeORM, Knex e sqlc não criam.

Relações

TipoFormaExemplo
Um-para-muitosFK no filhoorder_items.order_id → orders.id
Um-para-umFK + única, ou PK = FKuser_profiles.user_id
Muitos-para-muitosJunção <a>_<b>, PK compostauser_roles (user_id, role_id), pk_user_roles
Muitos-para-muitos com atributosNome de entidade significativamemberships, enrollments
Auto-referênciaparent_id ou papelcategories.parent_id, employees.manager_id

Pares polimórficos (commentable_type + commentable_id) não podem ter FKs. Prefira colunas FK nulas separadas com um check que exatamente uma está definida, ou tabelas separadas.

Esquemas, visões, funções, tipos

ObjetoPadrãoExemplo
Esquemadomínio ou móduloauth, billing, catalog, audit
Visãov_<nome>v_active_users
Visão materializadamv_<nome>mv_daily_sales
Função / procedimentoverbo + objetoget_user_balance, calculate_order_total
Tipo enum<entidade>_<atributo>order_status
Valores enumsnake_case minúsculopending, paid, refunded
Papel do banco<app>_<acesso>shop_app_rw, shop_app_ro, shop_migrator

Valores de enum não podem ser removidos sem recriar o tipo. Para conjuntos que mudam, prefira text + check ou uma tabela de consulta.

Tipos e defaults

  • timestamptz para instantes, default now(). CURRENT_TIMESTAMP AT TIME ZONE 'UTC' em uma coluna timestamptz é um bug.
  • text em vez de varchar(n) a menos que o comprimento seja uma regra de negócio; prefira check (char_length(col) <= n) para isso.
  • jsonb, nunca json.
  • numeric para dinheiro; nunca real, double precision ou money.
  • Novas colunas de identidade usam generated always as identity, não serial/bigserial.
  • Chaves primárias UUID usam v7: PostgreSQL 18 uuidv7(), .NET 9 Guid.CreateVersion7(), Node uuid package v7(), Go github.com/google/uuid uuid.NewV7(). Chaves v4 aleatórias fragmentam índices B-tree.
  • gen_random_uuid() é embutido desde PostgreSQL 13; uuid-ossp e pgcrypto não são necessários para isso.
  • citext ou um índice único lower(email) para unicidade insensível a maiúsculas/minúsculas.

Concorrência e padrões de consulta

  • Concorrência otimista, EF Core + Npgsql: uma propriedade uint configurada com IsRowVersion() mapeia para xmin.
  • Concorrência otimista, Node e Go: version integer not null default 0, atualizado com where id = $1 and version = $2 e version = version + 1; zero linhas afetadas significa um conflito. No sqlc use :execrows para obter a contagem de linhas afetadas.
  • select ... for update e for update skip locked apenas dentro de uma transação explícita; em autocommit o lock é liberado imediatamente.
  • Locks de aviso: pg_try_advisory_xact_lock dentro de uma transação. pg_advisory_lock em nível de sessão apenas em uma conexão dedicada mantida aberta até o desbloqueio; com pool, o lock e o unlock podem cair em conexões diferentes.
  • Listas grandes ou infinitas usam paginação por chave: where (created_at, id) < ($1, $2) order by created_at desc, id desc limit $3. offset apenas para listas pequenas e limitadas.
  • Concessões para tabelas futuras precisam de alter default privileges, não apenas grant ... on all tables.

Configuração ORM para snake_case

PilhaConfiguração
EF Core + NpgsqlEFCore.NamingConventions, UseSnakeCaseNamingConvention()
Drizzlecasing: 'snake_case'
Prisma@map nos campos, @@map nos modelos
TypeORMSnakeNamingStrategy de typeorm-naming-strategies
Knex / ObjectionknexSnakeCaseMappers()
Go + sqlcnenhuma; nomes Go são gerados de snake_case automaticamente

Se um projeto usa um ORM sem snake_case configurado, relate como Deve corrigir antes da primeira migração; após migrações existirem, relate como Recomendado com o custo da migração.

Go + sqlc

sqlc é SQL-first: o esquema vem dos arquivos de migração, então toda regra acima se aplica diretamente a eles.

  • Aponte schema em sqlc.yaml para o diretório de migrações (formatos goose, golang-migrate, dbmate e atlas são suportados; migrações down são ignoradas). Nunca mantenha um arquivo de esquema separado escrito à mão ao lado das migrações.
  • Tabelas no plural snake_case geram structs Go no singular: order_items → OrderItem. Enum order_status → tipo OrderStatus com constantes OrderStatusPending.
  • Use sql_package: pgx/v5. Sobrescreva uuid → github.com/google/uuid.UUID e, se desejado, timestamptz → time.Time em vez dos wrappers pgtype.
  • Corrija inicialismos com rename (url: URL, ip: IP) em vez de renomear colunas.
  • Nomes de consultas são PascalCase verbo + objeto, com o tipo de resultado:
PropósitoExemplo
Uma linha-- name: GetUserByEmail :one
Muitas linhas-- name: ListOrdersByCustomer :many
Inserir-- name: CreateOrder :one + returning *
Atualizar-- name: UpdateOrderStatus :execrows
Excluir-- name: DeleteSession :exec
Carga em massa-- name: CreateOrderItems :copyfrom
  • Nomeie parâmetros com @name ou sqlc.arg(name), e sqlc.narg(name) para os anuláveis; posicional $1 produz campos Go do tipo Column1.
  • Use sqlc.embed(table) para junções em vez de selecionar colunas com aliases ad-hoc.
  • Nunca use select * em consultas que devem sobreviver a mudanças de esquema, exceto returning * em escritas.

Habilidades Relacionadas