Why Data Modeling and API Design Matter More Than Ever in the Age of AI Code Review

AI has made writing and refactoring application logic dramatically faster. But that also means the most durable parts of your system — your data model, database schema, and API contracts — matter more than ever. These artifacts live for years, anchor many downstream services, and are expensive to change once released. Great code reviews in the AI era prioritize these long-lived contracts.
Why These Contracts Are More Durable Than Code
- High blast radius: Changing a column, enum, or endpoint shape breaks customers, internal consumers, and stored data.
- Migrations are costly: Data transformations, backfills, and dual-write windows are risky and time-consuming.
- Backward compatibility: Public APIs force deprecation windows and versioning strategies that outlive any single feature.
- AI accelerates the rest: With AI generating functions quickly, the relative value of a clean model and contract increases.
Clean Models Make AI Code Generation Better
- Clear types and invariants reduce ambiguity in prompts and lower hallucination risk.
- Well‑named entities and fields lead to higher‑quality completions and tests, because intent is obvious.
- Stable, documented APIs give AI solid constraints to generate against (OpenAPI/GraphQL schemas are powerful guardrails).
What To Prioritize in Code Review
Database & Data Model
- Names reflect domain concepts; avoid overloaded or generic names (e.g., `data`, `obj`).
- Normalization vs. denormalization trade‑offs are explicit; no hidden coupling.
- Constraints exist: primary keys, foreign keys, unique, not‑null, and check constraints.
- Indexes match query patterns; no accidental N+1 by design.
- Enums and status fields are well‑defined; transitions are validated.
- Soft deletes and multi‑tenant boundaries are consistent and auditable.
- Migrations are reversible, idempotent, and safe for large tables (batched backfills).
API Design
- Resource names, paths, and fields are consistent and predictable.
- Requests/responses documented via OpenAPI (or GraphQL SDL) with examples.
- Pagination, filtering, and sorting are consistent across endpoints.
- Error model is standardized (e.g., RFC 7807 problem+json) with stable codes.
- Idempotency for mutation endpoints (keys + retry semantics) where applicable.
- Versioning strategy is defined; breaking changes are avoided or versioned.
- AuthN/AuthZ, rate‑limits, and quota semantics are documented.
A Practical Review Checklist
- Schema migration reviewed with sample real data; indexes and constraints verified.
- OpenAPI/GraphQL schema diffed; run API linters (Spectral) and contract tests (Pact/Dredd).
- Backward‑compatibility: deprecations, versioning, and rollout plan documented.
- Performance: query plans sampled; N+1 risks called out; pagination required on lists.
- Ownership & lifecycle: who owns the table/endpoint; retention and deletion semantics.
- Telemetry: logs/metrics added for new critical paths and contract breakpoints.
Recommended Practices and Tools
- API Guidelines: Google API Design Guide, Microsoft REST API Guidelines, Stripe’s API design posts, JSON:API for JSON conventions.
- Contracts: OpenAPI/JSON Schema, GraphQL SDL; run Spectral for linting; add consumer‑driven contracts with Pact; use Dredd for spec‑to‑implementation tests.
- DB Migrations: Atlas, Prisma Migrate, Flyway, Liquibase; enable drift detection and review plans for large tables.
- Error Model: Adopt RFC 7807 for REST; with GraphQL, standardize errors and extensions fields.
OpenAPI Example: Bad vs. Better
The “bad” example is ambiguous, inconsistent, and lacks error shapes. The “better” example is explicit, consistent, and comes with examples and clear pagination.
# ❌ Bad (partial) openapi: 3.1.0 info: { title: API, version: 1.0.0 } paths: /listUsers: get: parameters: - in: query name: p schema: { type: integer } responses: '200': description: ok content: application/json: schema: { type: array } '500': { description: error }
# ✅ Better (partial) openapi: 3.1.0 info: title: Accounts API version: 1.2.0 components: schemas: Problem: type: object properties: type: { type: string, format: uri } title: { type: string } status: { type: integer } detail: { type: string } instance: { type: string, format: uri } User: type: object required: [id, email] properties: id: { type: string, format: uuid } email: { type: string, format: email } createdAt: { type: string, format: date-time } parameters: PageSize: { in: query, name: limit, schema: { type: integer, minimum: 1, maximum: 100 }, required: false } PageCursor: { in: query, name: cursor, schema: { type: string }, required: false } paths: /users: get: summary: List users parameters: [ { $ref: '#/components/parameters/PageSize' }, { $ref: '#/components/parameters/PageCursor' } ] responses: '200': description: A page of users headers: X-Next-Cursor: schema: { type: string } description: Cursor to fetch the next page content: application/json: schema: type: object properties: items: { type: array, items: { $ref: '#/components/schemas/User' } } count: { type: integer } examples: sample: value: items: [ { id: '2b1c...', email: 'a@example.com', createdAt: '2025-09-01T00:00:00Z' } ] count: 1 '400': { description: Bad request, content: { application/problem+json: { schema: { $ref: '#/components/schemas/Problem' } } } }
Spectral Linting: Quick Start
Lint your OpenAPI with Spectral to enforce naming, pagination, and error‑model rules in CI.
# .spectral.yaml (example) extends: ["spectral:oas"] rules: no-ambiguous-tag-names: warn operation-singular-tag: off prop-kebab-or-snake-case: description: properties use snake_case or lowerCamelCase consistently given: "$.components.schemas.*.properties.*~" then: function: pattern functionOptions: match: "^([a-z][a-zA-Z0-9]*|[a-z]+(_[a-z0-9]+)*)$" use-problem-json: description: errors must use RFC 7807 problem+json given: "$..responses.*.content.*~ ?(@property === 'application/problem+json')^" then: { function: truthy }
# package.json scripts { "scripts": { "lint:api": "spectral lint openapi.yaml" } } # Run locally npx spectral lint openapi.yaml
Contract Tests: Dredd (Spec → Implementation)
# dredd.yml (example) swagger: ./openapi.yaml endpoint: http://localhost:3000 hooks-worker-command: node language: nodejs
How To Spot Problems Quickly
- Smell: ambiguous naming. Ask for domain language. Rename before shipping to avoid permanent confusion.
- Smell: missing constraints. If logic enforces uniqueness, the database should too.
- Smell: undefined pagination or filtering. Lists must define limits, order, and cursors/links.
- Smell: ad‑hoc error payloads. Move to a standard shape and documented error codes.
- Smell: breaking changes without a plan. Require deprecations and staged rollouts.
Sources and Further Reading
- Google API Design Guide –cloud.google.com/apis/design
- Microsoft REST API Guidelines –github.com/microsoft/api-guidelines
- Stripe API Design –stripe.com/blog/api-design
- JSON:API Spec –jsonapi.org/format
- OpenAPI Initiative –openapis.org
- Spectral (OpenAPI linter) –docs.stoplight.io/docs/spectral
- Pact (Consumer‑Driven Contracts) –docs.pact.io
- Dredd (API testing against OpenAPI) –dredd.org
- RFC 7807: Problem Details for HTTP APIs –ietf.org/rfc7807
- HTTP Semantics (idempotency, RFC 9110) –rfc-editor.org/rfc/rfc9110
- GitHub API Pagination –docs.github.com … pagination
- Prisma Migrate –prisma.io/docs/orm/prisma-migrate
- Flyway / Liquibase –flywaydb.org /liquibase.org
- Atlas Schema Migrations –atlasgo.io
- Consumer‑Driven Contracts –martinfowler.com … consumerDrivenContracts
- Versioning –martinfowler.com … Versioning
Image: run this to generate a Japanese‑themed header image (logos allowed; no readable text):
./scripts/generate-blog-image-openai.sh 'data-modeling-api-design-ai-code-review' 'Zen‑inspired abstraction of data modeling and API design: clean tables, schema lines, and subtle endpoint diagrams; natural materials and negative space; no text' 'japanese-minimalist'
Then set the image path to /blog-data-modeling-api-design-ai-code-review.webp in blog metadata (already configured).
Level-up Your Code Reviews
Propel helps teams review the parts that matter most — data models, database changes, and API contracts — with context-aware suggestions and automated checks.