Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Opinionated backend development standards for Node.js + Express + TypeScript microservices. Covers layered architecture, BaseController pattern, dependency injection, Prisma repositories, Zod validation, unifiedConfig, Sentry error tracking, async safety, and testing discipline.
.claude/skills/dokhacgiakhoa-backend-dev-guidelines/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 42% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 38% | 0% |
| case-03 | ✗→✓ | ▲ Improved | -3% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 77% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 133% | 0% |
(Node.js · Express · TypeScript · Microservices)
You are a senior backend engineer operating production-grade services under strict architectural and reliability constraints.
Your goal is to build predictable, observable, and maintainable backend systems using:
This skill defines how backend code must be written, not merely suggestions.
Before implementing or modifying a backend feature, assess feasibility.
| Dimension | Question | | ----------------------------- | ---------------------------------------------------------------- | | Architectural Fit | Does this follow routes → controllers → services → repositories? | | Business Logic Complexity | How complex is the domain logic? | | Data Risk | Does this affect critical data paths or transactions? | | Operational Risk | Does this impact auth, billing, messaging, or infra? | | Testability | Can this be reliably unit + integration tested? |
BFRI = (Architectural Fit + Testability) − (Complexity + Data Risk + Operational Risk)Range: -10 → +10
| BFRI | Meaning | Action | | -------- | --------- | ---------------------- | | 6–10 | Safe | Proceed | | 3–5 | Moderate | Add tests + monitoring | | 0–2 | Risky | Refactor or isolate | | < 0 | Dangerous | Redesign before coding |
Automatically applies when working on:
Routes → Controllers → Services → Repositories → Databasets// ❌ NEVER router.post('/create', async (req, res) => { await prisma.user.create(...); }); // ✅ ALWAYS router.post('/create', (req, res) => userController.create(req, res) );
Routes must contain zero business logic.
BaseControllertsexport class UserController extends BaseController { async getUser(req: Request, res: Response): Promise<void> { try { const user = await this.userService.getById(req.params.id); this.handleSuccess(res, user); } catch (error) { this.handleError(error, res, 'getUser'); } } }
No raw res.json calls outside BaseController helpers.
tscatch (error) { Sentry.captureException(error); throw error; }
❌ console.log ❌ silent failures ❌ swallowed errors
ts// ❌ NEVER process.env.JWT_SECRET; // ✅ ALWAYS import { config } from '@/config/unifiedConfig'; config.auth.jwtSecret;
tsconst schema = z.object({ email: z.string().email(), }); const input = schema.parse(req.body);
No validation = bug.
src/
├── config/ # unifiedConfig
├── controllers/ # BaseController + controllers
├── services/ # Business logic
├── repositories/ # Prisma access
├── routes/ # Express routes
├── middleware/ # Auth, validation, errors
├── validators/ # Zod schemas
├── types/ # Shared types
├── utils/ # Helpers
├── tests/ # Unit + integration tests
├── instrument.ts # Sentry (FIRST IMPORT)
├── app.ts # Express app
└── server.ts # HTTP server| Layer | Convention | | ---------- | ------------------------- | | Controller | PascalCaseController.ts | | Service | camelCaseService.ts | | Repository | PascalCaseRepository.ts | | Routes | camelCaseRoutes.ts | | Validators | camelCase.schema.ts |
tsexport class UserService { constructor( private readonly userRepository: UserRepository ) {} }
tsawait userRepository.findActiveUsers();
All async route handlers must be wrapped.
tsrouter.get( '/users', asyncErrorWrapper((req, res) => controller.list(req, res) ) );
No unhandled promise rejections.
Every critical path must be observable.
tsdescribe('UserService', () => { it('creates a user', async () => { expect(user).toBeDefined(); }); });
No tests → no merge.
❌ Business logic in routes ❌ Skipping service layer ❌ Direct Prisma in controllers ❌ Missing validation ❌ process.env usage ❌ console.log instead of Sentry ❌ Untested business logic
Before finalizing backend work:
Status: Stable · Enforceable · Production-grade Intended Use: Long-lived Node.js microservices with real traffic and real risk
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 17,816 | 26,860 | +51% | 1 | 1 | 0% | 3,399 | 4,821 | +42% | 0 | 0 | — |
case-02 | fail→pass | 23,156 | 30,128 | +30% | 1 | 1 | 0% | 5,431 | 7,500 | +38% | 0 | 0 | — |
case-03 | fail→pass | 38,113 | 19,544 | -49% | 1 | 1 | 0% | 6,807 | 6,606 | -3% | 0 | 0 | — |
case-04 | fail→pass | 15,858 | 14,671 | -7% | 1 | 1 | 0% | 2,662 | 4,711 | +77% | 0 | 0 | — |
case-05 | fail→pass | 8,549 | 9,465 | +11% | 1 | 1 | 0% | 1,738 | 4,041 | +133% | 0 | 0 | — |
case-06 | fail→pass | 11,822 | 20,729 | +75% | 1 | 1 | 0% | 2,856 | 7,074 | +148% | 0 | 0 | — |
case-07 | fail→pass | 10,185 | 11,508 | +13% | 1 | 1 | 0% | 1,682 | 4,713 | +180% | 0 | 0 | — |
case-08 | fail→fail | 9,733 | 17,151 | +76% | 1 | 1 | 0% | 1,927 | 4,852 | +152% | 0 | 0 | — |
case-09 | fail→pass | 8,411 | 9,163 | +9% | 1 | 1 | 0% | 1,825 | 3,912 | +114% | 0 | 0 | — |
case-10 | fail→fail | 17,919 | 12,177 | -32% | 1 | 1 | 0% | 3,092 | 4,707 | +52% | 0 | 0 | — |
case-11 | fail→pass | 14,737 | 6,547 | -56% | 1 | 1 | 0% | 2,960 | 3,473 | +17% | 0 | 0 | — |
case-12 | fail→pass | 9,166 | 3,758 | -59% | 1 | 1 | 0% | 1,835 | 2,763 | +51% | 0 | 0 | — |
case-13 | fail→pass | 8,502 | 9,981 | +17% | 1 | 1 | 0% | 1,939 | 4,275 | +120% | 0 | 0 | — |
case-14 | pass→pass | 10,496 | 9,380 | -11% | 1 | 1 | 0% | 2,192 | 3,977 | +81% | 0 | 0 | — |
case-15 | fail→pass | 6,593 | 4,719 | -28% | 1 | 1 | 0% | 1,124 | 2,807 | +150% | 0 | 0 | — |
case-16 | pass→pass | 13,310 | 21,372 | +61% | 1 | 1 | 0% | 2,754 | 5,621 | +104% | 0 | 0 | — |
case-17 | fail→pass | 14,537 | 15,789 | +9% | 1 | 1 | 0% | 2,427 | 4,728 | +95% | 0 | 0 | — |
case-18 | fail→pass | 12,871 | 6,449 | -50% | 1 | 1 | 0% | 2,226 | 3,235 | +45% | 0 | 0 | — |
case-19 | pass→pass | 12,466 | 8,046 | -35% | 1 | 1 | 0% | 2,320 | 3,491 | +50% | 0 | 0 | — |
case-20 | pass→pass | 9,198 | 10,802 | +17% | 1 | 1 | 0% | 1,671 | 4,068 | +143% | 0 | 0 | — |
case-21 | pass→pass | 13,226 | 15,100 | +14% | 1 | 1 | 0% | 2,473 | 4,570 | +85% | 0 | 0 | — |
case-22 | pass→pass | 12,069 | 17,015 | +41% | 1 | 1 | 0% | 2,386 | 4,653 | +95% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +64 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.