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.