Skip to main content

SDLC 03: Development Standards and Implementation Guidelines

Revision history: Updated May 2026 — AWS EC2 hosting; Express + Next.js split; PawaPay + Flutterwave payment standards; CommonJS module conventions.


The Pakashop repository is organized as a monorepo containing a backend Express API and a frontend Next.js application. Supporting documents (SDLC, API) are version-controlled alongside the code to ensure documentation stays synchronized with the implementation.


2. Coding Standards

2.1 Module System

The backend uses CommonJS (require / module.exports) throughout. The frontend uses ES Modules (import / export).

  • Backend: Uses CommonJS (require / module.exports) to maintain compatibility with the Node.js runtime and existing microservices.
  • Frontend: Uses ES Modules (import / export) for modern build-time optimizations (tree-shaking).

Critical: Never mix require and import within the same backend file. New backend files must use CommonJS.

2.2 Import Path Conventions

Backend imports follow these relative depth rules:

File locationTo reach lib/Example
src/routes/../lib/require('../lib/logger')
src/controllers/feature/../../lib/require('../../lib/prisma')
src/services/feature/../../lib/require('../../lib/logger')
src/services/payment/webhooks/../../../lib/require('../../../lib/prisma')

Always verify the relative depth before adding a new require(). Incorrect paths are the most common source of startup crashes.

2.3 Structured Logging

Use the centralised pino logger. Never use console.log in production code.

All logs use the pino structured logger and are shipped to Middleware.io. PII (phone numbers, emails, card data) must be masked at the application layer before any log write occurs.

All logs are shipped to Middleware.io via OpenTelemetry. PII (phone numbers, emails, card data) must be masked before logging — use ComplianceLogger.maskPhone() / ComplianceLogger.maskEmail().

2.4 Observability — Custom Spans

Wrap critical business logic in withSpan to generate traces visible in Middleware.io:

Critical business logic is wrapped in custom spans to provide granular visibility into operation duration and success rates within the APM dashboard.

2.5 Error Handling

  • Controller functions must never throw unhandled errors to Express. Always wrap in try/catch.
  • Return consistent error shapes: { success: false, message: '...' }.
  • Attach statusCode to thrown errors so controllers can forward the correct HTTP status:

Controllers catch exceptions and format them into a consistent error shape (success, message, code) with appropriate HTTP status codes (e.g., 404 for missing resources).

2.6 Prisma

  • Run npx prisma generate after every schema change.
  • All database queries use the Prisma client — no raw SQL.
  • For transactions, use prisma.$transaction([...]) or the interactive client prisma.$transaction(async (tx) => { ... }).
  • Development: npx prisma migrate dev --name <description>.
  • Production: npx prisma migrate deploy.
  • Never use prisma db push in production (it bypasses migration history).

2.7 Payment — Critical Rules

  • Card Security: All card entry must occur on hosted gateway pages; Pakashop servers are prohibited from handling raw card data.
  • Data Privacy: Phone numbers and emails must be masked before being written to any persistent logs.

3. Environment Variables

Variables are stored in /opt/pakashop/.env.production on EC2 (backend) and Vercel Environment Variables (frontend). Never commit .env files.

The .env.example at the project root documents all required variables with placeholder values.

ScopeManagement method
Backend (EC2)/path/to/.env.production loaded by systemd
Frontend (Vercel)Vercel Project Settings → Environment Variables
Local dev.env.local (gitignored)

4. Implementation Guidelines by Module

4.1 Authentication

  • JWT accessToken (15 min TTL) + refreshToken (7 days, stored in Redis).
  • OTP login via Resend; Google OAuth via OAuthService.js.
  • Always use the authenticate middleware on protected routes.
  • Role checks via requireRoles('PLATFORM_ADMIN').

4.2 File Uploads

  • Uploads go to Cloudinary via signed URL (never direct from client without signature).
  • After upload, trigger the moderation service asynchronously (fire-and-forget).
  • Maximum sizes: 10 MB for product images; 5 MB for KYC documents.
  • Accepted MIME types validated by multer before any Cloudinary call.

4.3 Payments

  • All payment orchestration goes through PaymentService (the only module the rest of the app imports).
  • Use PAYMENT_PROVIDER=MOCK in local development — no real gateway calls.
  • Mobile money: always validate Zambian phone format before calling the gateway.
  • Card: always use type: 'redirect' response — never collect card fields.

4.4 Multi-Vendor Order Handling

  • Each OrderItem is linked to a Shop and carries its own settlementStatus.
  • The SettlementLedger calculates vendorAmount and platformFee per item.
  • Only an admin action (POST /payments/release/:orderId) transitions HELD → RELEASABLE.

4.5 SEO

  • Dynamic <title> and <meta name="description"> via Next.js generateMetadata() on every page.
  • Structured data (JSON-LD) on product detail pages.
  • sitemap.xml generated programmatically; submitted to Google Search Console.

5. Git Workflow

BranchPurposeMerge policy
feature/*New featuresPR → main; requires 1 review + CI pass
hotfix/*Critical production fixesPR → production; fast-track review
chore/*Dependency updates, docsPR → main
mainIntegration / stagingProtected; no direct push
productionLive platformProtected; triggers EC2 deploy via GitHub Actions

Commit message format:

type(scope): short description

Types: feat, fix, chore, docs, refactor, test, style
Example: feat(payments): add PawaPay USSD overlay component