Code Quality
Patterns, anti-patterns, technical debt, and maintainability issues.
Architectural Issues
Business Logic in Mongoose Hooks
The most complex business logic is embedded in Mongoose pre/post hooks on models (especially Ledger, License, Invoice, Booking). This makes it:
- Hard to test — logic is tightly coupled to database operations
- Hard to debug — hooks fire implicitly, creating non-obvious side effects
- Hard to reason about — a single
save()call can trigger chains of hooks across models
Example: The Ledger model's pre-save hook alone handles: currency derivation, org validation, permission checks, fund validation, wallet splitting, overflow logic, and parking wallet auto-creation.
Recommendation: Extract to explicit service layer functions that can be tested independently.
Mixed Authentication Patterns
Three different auth mechanisms with unclear precedence:
- Session-based — Express sessions with Redis store
- API key —
x-api-keyheader for service-to-service - JWT tokens — in query parameters for login flows
No unified authentication middleware enforces consistency or priority. Different routes use different mechanisms ad-hoc.
Tight Coupling Between Packages
Despite being a monorepo with separate packages, the services share models directly via file paths. The openmembers package imports models from ../../models/ directly rather than going through the API.
Impact: Cannot deploy or test packages independently. Changes to models affect all services simultaneously.
eval() in Cron Scheduler
packages/cron/cron.js
Schedule records store JavaScript code strings in the action field, executed at runtime via eval(). This is a code injection risk if the database is compromised, and makes it impossible to statically analyse what the cron system does.
Code Quality Issues
No Input Validation
The codebase lacks input validation middleware. req.body is passed directly to database operations without schema validation.
// CURRENT - no validation
const organisation = await req.apihelper.getOne("organisation", req.params.organisation_id);
if (!req.body.industrysector_id) { req.body.industrysector_id = [] }
await req.apihelper.put("organisation", req.session.user.organisation_id, req.body);
Recommendation: Add schema validation (Zod, Joi, or express-validator) at every API boundary.
Inadequate Error Handling
Multiple patterns of poor error handling:
packages/api/libs/messagequeue.js:console.error()with no re-throw (errors silently swallowed)packages/openmembers/routes/login.js: Generic error messages provide no debugging infopackages/api/bin/server.js: MongoDB errors only logged, not handled
Errors may contain sensitive information in some paths, while being swallowed entirely in others.
Inconsistent Secret Management
Secrets are sourced from at least four different locations with no centralised management:
| Source | Usage |
|---|---|
config.secret | Session encryption in some modules |
config.openmembers.secret | OpenMembers-specific encryption |
process.env.APIKEY | API authentication in some routes |
| JSON keyfiles on disk | Google Cloud credentials |
No rotation mechanism exists for any of these secrets.
Mixed Frontend Technologies
The platform uses three different frontend approaches:
- Handlebars — server-rendered templates (OpenMembers, CRM)
- Svelte — Xero integration, Doors module
- Vue 2 — POS, some components
This creates cognitive overhead for developers and inconsistent user experiences.
Legacy Dependencies
- PhantomJS 2.1.16 — deprecated headless browser, used for PDF generation
- Node 8 "carbon" — in OpenMembers Dockerfile (EOL Dec 2019)
- Node 16 — in API Dockerfile (EOL Sept 2023)
- Vue 2 — end of life (Dec 2023)
- Requires
--openssl-legacy-providerflag for Webpack builds
Test Coverage
Minimal Test Coverage
For a platform of this size and complexity, the test suite is notably sparse:
- ~9 test files total across the entire codebase
- API tests cover basic CRUD and login only
- No security-focused tests (auth bypass, injection, etc.)
- No integration tests for complex workflows (invoice run, booking lifecycle)
- Selenium tests exist but cover limited UI scenarios
- No tests for the wallet/ledger deduction logic (the most complex code)
What exists:
| Suite | Command | Scope |
|---|---|---|
| API | yarn test:api | Basic CRUD, login |
| OpenMembers | yarn test:openmembers | Limited |
| Shared | yarn test:shared | Shared utilities |
| Reports | yarn test:reports | Report generation |
| Selenium | yarn test:selenium | Basic UI flows |
Positive Patterns Worth Preserving
Not everything is technical debt. These patterns work well and should inform the replacement:
| Pattern | Where | Why It Works |
|---|---|---|
| Audit logging via deep-diff | Log model, post-validate hooks | Every model change is tracked with before/after diffs. Critical for financial compliance. |
| Reserve-to-debit pattern | Ledger/Booking models | Elegant approach to hold funds during booking without immediate deduction. |
| Track-based task templates | Opportunity/Task/Track models | Templated sales processes with auto-generated task chains and cascading due dates. |
| Multi-instance federation | External services, Lead model | Users can be looked up across multiple WSM instances, enabling network-wide access. |
| Configurable feature set | OpenMembers config "components" array | Features can be enabled/disabled per deployment without code changes. |
| Priority-based wallet deduction | Wallet/Ledger models | Flexible credit system supporting multiple currencies with overflow between wallets. |
Technical Debt Summary
| Category | Issue Count | Impact |
|---|---|---|
| Architecture | 4 | Limits scalability, testability, and independent deployment |
| Code Quality | 4 | Increases bug risk, slows development, creates security gaps |
| Testing | 1 (systemic) | No safety net for refactoring or adding features |
| Dependencies | 5+ | Known CVEs in EOL runtimes and libraries |
| Frontend | 3 frameworks | Developer cognitive overhead, inconsistent UX |