Skip to main content

Code mistakes

Structure problems we found when we reviewed our own APIs in October 2026. Each one cost time or caused a bug. Read it in your first week.

The pattern behind most of them​

A rule nobody can follow is worse than no rule: it hides the real structure. When a rule gets in the way, change the rule in the handbook.

Structure​

  • About 190 database imports inside "framework-free" use-cases, almost all to open a transaction. Now use-cases may use withTransaction.
  • Modules imported each other's tables despite a bridge-only rule. Now modules import each other's public index.ts, and a lint rule blocks deep imports.
  • One lookup repository copied into three modules, each reading another module's schema.
  • Composition files of 1,100 to 1,800 lines, and a 4,000-line settings file.
  • Resolver files named after delivery slices (slice-8e).
  • A Java API that called itself hexagonal was layered by technical layer, with repositories nested under database model folders.

Consistency across repos​

  • Four error envelopes in four APIs. Pick one per company.
  • Five ways to reach the database: an ORM behind ports, raw SQL in routes, a different ORM, JPA, and inline SQL in route handlers for reads.
  • Module file names differed inside one repo: api.py, routes.py, router.py, routers/; model.py, models.py, models/.
  • Docs and folder names drifted after renames; product names in folders differed between two services of the same product.

Validation and errors​

  • Ids were not validated, so junk reached Postgres and showed up as server errors.
  • One module validated with Zod, the next with hand-written checks.

Tests and data​

  • Fake databases hid real bugs in the most important path we had.
  • Real-looking names in seed migrations.