Skip to content

Nonfunctional Analysis Examples

Sample findings and recommendations.


Testing Coverage Examples

Example 1: Critical Path Without Tests

Finding: Payment processing endpoint has no test coverage.

Evidence:

src/routes/payments.ts
├── POST /api/payments/charge     ✗ No tests
├── POST /api/payments/refund     ✗ No tests
└── GET  /api/payments/:id        ✓ Tested

Impact: High - Payment failures may go undetected until production.

Recommendation:

// tests/routes/payments.test.ts
describe('POST /api/payments/charge', () => {
  it('should process valid payment', async () => {
    const response = await request(app)
      .post('/api/payments/charge')
      .send({ amount: 1000, currency: 'USD', token: 'tok_valid' });

    expect(response.status).toBe(200);
    expect(response.body.status).toBe('succeeded');
  });

  it('should reject invalid payment token', async () => {
    const response = await request(app)
      .post('/api/payments/charge')
      .send({ amount: 1000, currency: 'USD', token: 'tok_invalid' });

    expect(response.status).toBe(400);
  });
});


Example 2: Flaky Test Detection

Finding: Test relies on timing, causing intermittent failures.

Evidence:

// src/services/__tests__/cache.test.ts:45
it('should expire after TTL', async () => {
  cache.set('key', 'value', { ttl: 100 });
  await sleep(150); // Flaky: timing-dependent
  expect(cache.get('key')).toBeUndefined();
});

Impact: Medium - CI failures cause developer friction.

Recommendation:

it('should expire after TTL', async () => {
  const mockTime = jest.useFakeTimers();
  cache.set('key', 'value', { ttl: 100 });

  mockTime.advanceTimersByTime(150);

  expect(cache.get('key')).toBeUndefined();
  mockTime.useRealTimers();
});


Example 3: Test Without Assertions

Finding: Test passes but verifies nothing meaningful.

Evidence:

// src/utils/__tests__/format.test.ts:23
it('should format currency', () => {
  const result = formatCurrency(1000);
  console.log(result); // No assertion!
});

Impact: Low - False confidence in test coverage.

Recommendation:

it('should format currency with dollar sign and commas', () => {
  expect(formatCurrency(1000)).toBe('$1,000.00');
  expect(formatCurrency(1000000)).toBe('$1,000,000.00');
  expect(formatCurrency(0.5)).toBe('$0.50');
});


Configuration Audit Examples

Example 1: Hardcoded Secret

Finding: API key hardcoded in source code.

Evidence:

// src/services/stripe.ts:5
const stripe = new Stripe('sk_live_abc123xyz789...', {
  apiVersion: '2023-10-16'
});

Impact: Critical - Secret exposed in version control.

Recommendation:

// src/services/stripe.ts
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
  apiVersion: '2023-10-16'
});

// .env.example
STRIPE_SECRET_KEY=sk_test_... # Replace with your key


Example 2: Missing Configuration Validation

Finding: Application starts without validating required config.

Evidence:

// src/config.ts
export const config = {
  databaseUrl: process.env.DATABASE_URL,
  redisUrl: process.env.REDIS_URL,
  jwtSecret: process.env.JWT_SECRET,
};

// No validation - undefined values will cause runtime errors

Impact: High - Application may start with missing config, fail later.

Recommendation:

// src/config.ts
import { z } from 'zod';

const configSchema = z.object({
  databaseUrl: z.string().url(),
  redisUrl: z.string().url(),
  jwtSecret: z.string().min(32),
});

export const config = configSchema.parse({
  databaseUrl: process.env.DATABASE_URL,
  redisUrl: process.env.REDIS_URL,
  jwtSecret: process.env.JWT_SECRET,
});

// Now fails fast at startup with clear error


Example 3: Inconsistent Environment Handling

Finding: Different config patterns across the codebase.

Evidence:

// src/db.ts - Uses config object
import { config } from './config';
const db = connect(config.databaseUrl);

// src/cache.ts - Direct env access
const cache = connect(process.env.REDIS_URL);

// src/email.ts - Hardcoded fallback
const apiKey = process.env.SENDGRID_KEY || 'SG.default';

Impact: Medium - Maintenance burden, inconsistent behavior.

Recommendation:

// Centralize all config access
// src/config.ts
export const config = {
  database: { url: process.env.DATABASE_URL },
  redis: { url: process.env.REDIS_URL },
  email: { apiKey: process.env.SENDGRID_KEY },
};

// All files import from config
import { config } from './config';


Performance Hotspot Examples

Example 1: N+1 Query

Finding: Database query inside loop creates N+1 problem.

Evidence:

// src/services/order.ts:45
async function getOrdersWithProducts(userId: string) {
  const orders = await Order.findAll({ where: { userId } });

  // N+1: One query per order
  for (const order of orders) {
    order.products = await Product.findAll({
      where: { orderId: order.id }
    });
  }

  return orders;
}

Impact: High - O(n) database queries, severe at scale.

Recommendation:

async function getOrdersWithProducts(userId: string) {
  // Single query with eager loading
  const orders = await Order.findAll({
    where: { userId },
    include: [{ model: Product }]
  });

  return orders;
}


Example 2: Sequential Awaits

Finding: Independent async operations run sequentially.

Evidence:

// src/controllers/dashboard.ts:23
async function getDashboardData(userId: string) {
  const user = await getUser(userId);           // 100ms
  const orders = await getOrders(userId);       // 150ms
  const notifications = await getNotifications(userId); // 80ms
  // Total: 330ms sequential

  return { user, orders, notifications };
}

Impact: Medium - Unnecessary latency.

Recommendation:

async function getDashboardData(userId: string) {
  const [user, orders, notifications] = await Promise.all([
    getUser(userId),           // 100ms
    getOrders(userId),         // 150ms  } parallel
    getNotifications(userId),  // 80ms   }
  ]);
  // Total: 150ms (longest operation)

  return { user, orders, notifications };
}


Example 3: Missing Await

Finding: Promise not awaited, operation may not complete.

Evidence:

// src/services/audit.ts:34
async function processOrder(order: Order) {
  // Business logic...

  auditLog.save({ action: 'order_processed', orderId: order.id });
  // Missing await! Audit log may not be saved before function returns

  return order;
}

Impact: Medium - Data loss possible if process exits.

Recommendation:

async function processOrder(order: Order) {
  // Business logic...

  await auditLog.save({ action: 'order_processed', orderId: order.id });

  return order;
}

// Or if fire-and-forget is intentional, handle errors:
auditLog.save({ ... }).catch(err => logger.error('Audit failed', err));


Example 4: High Complexity Function

Finding: Function exceeds complexity thresholds.

Evidence:

// src/services/pricing.ts:78
// Cyclomatic: 25, Cognitive: 38, Lines: 120
function calculatePrice(product, user, promotions, context) {
  let price = product.basePrice;

  if (user.isPremium) {
    if (context.isHoliday) {
      if (promotions.hasDiscount) {
        // Deep nesting continues...
      }
    }
  }
  // ... 100 more lines
}

Impact: High - Difficult to maintain, prone to bugs.

Recommendation:

// Break into smaller functions
function calculatePrice(product, user, promotions, context) {
  const basePrice = getBasePrice(product);
  const userDiscount = getUserDiscount(user);
  const promoDiscount = getPromoDiscount(promotions, context);
  const contextAdjustment = getContextAdjustment(context);

  return applyDiscounts(basePrice, [
    userDiscount,
    promoDiscount,
    contextAdjustment
  ]);
}


Code Health Examples

Example 1: Code Duplication

Finding: Error handling duplicated across controllers.

Evidence:

// src/controllers/user.ts:45
try {
  const user = await userService.create(data);
  res.json({ success: true, data: user });
} catch (error) {
  logger.error('User creation failed', error);
  if (error instanceof ValidationError) {
    res.status(400).json({ error: error.message });
  } else {
    res.status(500).json({ error: 'Internal server error' });
  }
}

// src/controllers/order.ts:67 - DUPLICATE
try {
  const order = await orderService.create(data);
  res.json({ success: true, data: order });
} catch (error) {
  logger.error('Order creation failed', error);
  if (error instanceof ValidationError) {
    res.status(400).json({ error: error.message });
  } else {
    res.status(500).json({ error: 'Internal server error' });
  }
}

Impact: Medium - Maintenance burden, inconsistent fixes.

Recommendation:

// src/middleware/errorHandler.ts
export function asyncHandler(fn) {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
}

export function errorHandler(err, req, res, next) {
  logger.error('Request failed', err);

  if (err instanceof ValidationError) {
    return res.status(400).json({ error: err.message });
  }

  res.status(500).json({ error: 'Internal server error' });
}

// Controllers become simple
router.post('/users', asyncHandler(async (req, res) => {
  const user = await userService.create(req.body);
  res.json({ success: true, data: user });
}));


Example 2: Dead Code

Finding: Exported function not used anywhere.

Evidence:

// src/utils/format.ts
export function formatCurrency(amount: number) { ... }  // Used
export function formatDate(date: Date) { ... }          // Used
export function formatPhoneNumber(phone: string) { ... } // NOT USED

// No imports of formatPhoneNumber found in codebase

Impact: Low - Minor code bloat.

Recommendation:

// Remove unused export or mark as intentionally kept
// Option 1: Remove
// export function formatPhoneNumber(phone: string) { ... }

// Option 2: If needed for future/external use
/** @internal Reserved for future phone validation feature */
export function formatPhoneNumber(phone: string) { ... }


Example 3: Technical Debt Marker

Finding: FIXME comment indicates known bug.

Evidence:

// src/services/auth.ts:89
// FIXME: Race condition when multiple login attempts
// This has been here for 8 months
async function handleLogin(credentials) {
  const session = await getSession(credentials.userId);
  if (session) {
    await deleteSession(session.id);  // Race condition here
  }
  return createSession(credentials.userId);
}

Impact: High - Known bug affecting production reliability.

Recommendation:

async function handleLogin(credentials) {
  // Use atomic operation to prevent race condition
  return await db.transaction(async (trx) => {
    await Session.destroy({
      where: { userId: credentials.userId },
      transaction: trx
    });
    return Session.create({
      userId: credentials.userId
    }, { transaction: trx });
  });
}


Example 4: Naming Inconsistency

Finding: Mixed naming conventions in codebase.

Evidence:

src/
├── userService.ts      # camelCase
├── OrderService.ts     # PascalCase
├── payment_service.ts  # snake_case
├── email-service.ts    # kebab-case
└── AuthService.ts      # PascalCase

Impact: Low - Developer confusion, harder onboarding.

Recommendation:

# Standardize on one convention (e.g., kebab-case for files)
src/
├── user-service.ts
├── order-service.ts
├── payment-service.ts
├── email-service.ts
└── auth-service.ts

# Add lint rule to enforce
// .eslintrc
{
  "rules": {
    "unicorn/filename-case": ["error", { "case": "kebabCase" }]
  }
}


Summary Finding Format

Each finding should follow this structure:

### [Category]: [Brief Description]

**Finding**: [What was found]

**Evidence**:
```code
[Relevant code snippet with file location]

Impact: [Critical/High/Medium/Low] - [Why it matters]

Recommendation:

[How to fix it]
```