SPB Git

spb/ultra-sharp-agent-skills Public

Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.

Python 100%

# name: modeling-nosql-data description: Models data for document and key-value stores — access-pattern-first design, embed vs reference decisions, controlled denormalization, Redis key design, cursor pagination, and document schema versioning. Use when the user asks to design a MongoDB or DynamoDB schema, model documents or collections, structure Redis keys, decide between embedding and referencing, or migrate a relational model to NoSQL. Do not use for relational schema design or for SQL databases with JSON columns, which follow relational rules first.

# Modeling NoSQL Data

# When to use / when NOT to use

  • Use for: designing document models (MongoDB, Firestore, DynamoDB) and key-value layouts (Redis, DynamoDB keys).
  • Do NOT use for: relational schemas (designing-database-schemas) — and a Postgres table with a JSONB column is still relational: normalize first, JSONB for the truly schemaless remainder.

# Core rules

  1. Queries first, schema second. List every access pattern (screen/endpoint → fields → frequency) BEFORE shaping documents — the inverse of relational design. A model that can't be listed against its queries isn't done.
  2. Embed vs reference decision rule:
    • Embed when data is 1:few, read together, and updated together (order + its line items).
    • Reference when 1:many grows unbounded, is shared by many parents, or updates independently (user ← posts; product ← reviews).
    • {order_id, items: [{sku, qty}]} — ❌ embedding a user's entire post history in the user document.
  3. Denormalize on purpose, with ONE source of truth per fact. Copying author_name into posts is fine — but document where the canonical value lives and how copies get refreshed (on write, or accept staleness with a TTL).
  4. Design far below size limits. MongoDB caps documents at 16MB; treat ~1MB as the design ceiling. Unbounded arrays are the failure smell — bucket them (one document per day/100 items) or reference.
  5. Redis/KV keys: colon-namespaced, predictable, with a TTL policy.
    • user:42:session, cache:product:9f3e + EXPIRE
    • data_42_final, keys without owner/type, cache keys with no TTL.
  6. Paginate with cursors, never offsets. Sort by an indexed, unique (or tie-broken) field and continue from the last seen value: find({created_at: {$lt: cursor}}).limit(20).
  7. Version documents. Every document carries schema_version: 3; readers handle N and N-1, writers upgrade on write (lazy migration).
  8. Model transactions around aggregates. Put data that must change atomically in ONE document; cross-document transactions exist but are the escape hatch, not the design.

# Workflow

  1. Write the access-pattern table: operation, fields needed, reads/sec vs writes/sec, consistency need.
  2. Group into aggregates (what changes together) → those become documents; everything else becomes references or copies.
  3. For each copy created by denormalization, record the source of truth and the refresh rule.
  4. Define keys/indexes per access pattern (compound indexes matching sort + filter; Redis key format + TTL per type).
  5. Validate: walk EVERY access pattern from step 1 against the model — each must resolve to one indexed query or one key lookup. Any pattern needing a scan or N+1 fetches → reshape and re-check.

# Edge cases & failure modes

  • Many-to-many → reference both ways only if both directions are queried; otherwise store the relation on the side that's queried.
  • Hot documents (one doc absorbing all writes, e.g. a global counter) → shard the key (counter:{0..15}) and sum on read.
  • Search across fields → document stores are poor at ad-hoc search; pair with a search index rather than contorting the model.
  • Strong consistency needed across entities → that's the signal you may be in relational territory; say so instead of forcing it.
  • Relational→NoSQL migration → do NOT port tables 1:1 into collections; restart from access patterns (rule 1).

# References

Worked examples (blog, orders, sessions), bucket pattern, cursor pagination code, Redis key catalog: see references/patterns.md.