Contributing Guide - jra3/mulm GitHub Wiki

Contributing Guide

Thank you for your interest in contributing to the Mulm Breeder Awards Program platform! This guide will help you get started.

Table of Contents

  1. Code of Conduct
  2. Getting Started
  3. Development Workflow
  4. Code Style Guidelines
  5. Testing Requirements
  6. Submitting Changes
  7. Review Process

Code of Conduct

Our Pledge

We are committed to providing a welcoming and inclusive environment for all contributors, regardless of experience level.

Expected Behavior:

  • โœ… Be respectful and constructive
  • โœ… Focus on what's best for the project
  • โœ… Accept constructive criticism gracefully
  • โœ… Help newcomers get started

Unacceptable Behavior:

  • โŒ Harassment or discriminatory comments
  • โŒ Trolling or intentionally derailing discussions
  • โŒ Publishing others' private information

Enforcement: Violations may result in temporary or permanent ban from the project.


Getting Started

Prerequisites

Before contributing, ensure you have:

Finding Something to Work On

For beginners:

  • Look for issues labeled good first issue
  • Documentation improvements
  • Test coverage additions
  • Bug fixes with clear reproduction steps

For experienced developers:

  • Issues labeled help wanted
  • Feature requests
  • Performance improvements
  • Security enhancements

Check with maintainers first for large features to ensure alignment with project direction.


Development Workflow

1. Fork and Clone

# Fork the repository on GitHub
# Then clone your fork
git clone https://github.com/YOUR-USERNAME/mulm.git
cd mulm

# Add upstream remote
git remote add upstream https://github.com/jra3/mulm.git

2. Create a Branch

# Update main branch
git checkout main
git pull upstream main

# Create feature branch
git checkout -b feature/your-feature-name

# Or for bug fixes
git checkout -b fix/bug-description

Branch naming:

  • feature/ - New features
  • fix/ - Bug fixes
  • docs/ - Documentation updates
  • refactor/ - Code refactoring
  • test/ - Test additions

3. Make Your Changes

# Start dev server
npm run dev

# Make changes
# Server hot-reloads automatically

# Run tests
npm test

# Run linter
npm run lint

4. Commit Your Changes

Commit message format:

type(scope): brief description

Longer explanation of what changed and why.

- Bullet points for details
- Multiple lines if needed

Fixes #123

Types:

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation only
  • style - Code style (formatting, no logic change)
  • refactor - Code refactoring
  • test - Adding or updating tests
  • chore - Build process, dependencies

Examples:

git commit -m "feat(submissions): add photo upload support

Implements image upload functionality using Cloudflare R2 storage
with Sharp image processing for optimization.

- Adds /api/upload/image endpoint
- Processes images into 3 size variants
- Strips EXIF data for privacy
- Includes upload progress tracking via SSE

Fixes #45"
git commit -m "fix(admin): correct witness confirmation button alignment

The confirm/decline buttons were misaligned on mobile. Updated
Tailwind classes to use flexbox layout.

Fixes #78"

5. Push and Create PR

# Push your branch
git push origin feature/your-feature-name

# Create pull request on GitHub
# Fill out the PR template

Code Style Guidelines

TypeScript

Strict type checking enabled:

// โœ… Good - explicit types
async function getMember(id: number): Promise<MemberRecord | null> {
  return await query<MemberRecord>('SELECT * FROM members WHERE id = ?', [id]);
}

// โŒ Bad - implicit any
async function getMember(id) {
  return await query('SELECT * FROM members WHERE id = ?', [id]);
}

Use async/await (not callbacks):

// โœ… Good
const results = await db.all('SELECT * FROM members');

// โŒ Bad
db.all('SELECT * FROM members', (err, results) => {
  // Callback style - don't use
});

Always finalize prepared statements:

// โœ… Good
const stmt = await db.prepare('SELECT * FROM members WHERE id = ?');
try {
  const result = await stmt.get(id);
  return result;
} finally {
  await stmt.finalize(); // Always finalize
}

// โŒ Bad - missing finalize (memory leak)
const stmt = await db.prepare('SELECT * FROM members WHERE id = ?');
return await stmt.get(id);

Pug Templates

CRITICAL Rules:

// โœ… Good - double quotes, broken class chains
div(
  class="bg-gradient-to-r from-yellow-50 to-amber-50" +
        " rounded-lg shadow-lg p-6"
)

// โŒ Bad - single quotes, long chains
div(class='bg-gradient-to-r from-yellow-50 to-amber-50 rounded-lg shadow-lg p-6')

Modifiers (hover:, md:, focus:):

// โœ… Good - use class attribute
div(class="hover:bg-blue-500 md:flex focus:outline-none")

// โŒ Bad - dot notation with modifiers
div.hover:bg-blue-500.md:flex

Simple utilities only with dot notation:

// โœ… Good - simple utilities
div.flex.gap-4.items-center

// โŒ Bad - modifiers with dots
div.md:flex.hover:bg-blue-500

SVG viewBox:

// โœ… Good - lowercase
svg(viewbox="0 0 24 24")

// โŒ Bad - camelCase
svg(viewBox="0 0 24 24")

See: CLAUDE.md - Pug Template Guidelines

Database Queries

Always use prepared statements:

// โœ… Good - parameterized query
const results = await query<Member>(
  'SELECT * FROM members WHERE contact_email = ?',
  [email]
);

// โŒ Bad - string concatenation (SQL injection risk)
const results = await query<Member>(
  `SELECT * FROM members WHERE contact_email = '${email}'`
);

Use transactions for multiple writes:

// โœ… Good - atomic operation
await withTransaction(async (db) => {
  await db.run('INSERT INTO members ...');
  await db.run('INSERT INTO submissions ...');
  // Both succeed or both rollback
});

// โŒ Bad - separate operations (not atomic)
await db.run('INSERT INTO members ...');
await db.run('INSERT INTO submissions ...');

ESLint

# Check for issues
npm run lint

# Auto-fix where possible
npm run lint:fix

Key rules:

  • No unused variables
  • Consistent quote style (single quotes for strings, double for JSX/templates)
  • Semicolons required
  • Proper async/await usage

Testing Requirements

All Code Changes Must Include Tests

New features:

  • Unit tests for new utilities/functions
  • Integration tests for database operations
  • End-to-end tests for workflows

Bug fixes:

  • Regression test demonstrating the bug
  • Verification test showing fix works

Refactoring:

  • Existing tests must still pass
  • Add tests for edge cases if uncovered

Writing Tests

// src/__tests__/myfeature.test.ts
import { describe, test, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert';
import { setupTestDatabase } from './testDbHelper.helper';

describe('My Feature', () => {
  let testDb;

  beforeEach(async () => {
    testDb = await setupTestDatabase();
  });

  afterEach(async () => {
    await testDb.cleanup();
  });

  test('should do something specific', async () => {
    // Arrange
    const input = 'test data';

    // Act
    const result = await myFunction(input);

    // Assert
    assert.strictEqual(result, expectedValue);
  });
});

See: Testing Guide for comprehensive testing documentation.

Running Tests Before Submitting

# Run all tests
npm test

# Should see: All tests passed

# Run linter
npm run lint

# Should see: No errors found

PR will be rejected if:

  • โŒ Tests are failing
  • โŒ Linter shows errors
  • โŒ New code has no tests

Submitting Changes

Pull Request Checklist

Before submitting a PR, ensure:

  • Code follows style guidelines
  • All tests pass (npm test)
  • Linter passes (npm run lint)
  • New tests added for new features/fixes
  • Documentation updated (if needed)
  • Commit messages follow format
  • Branch is up to date with main

Pull Request Template

## Description
Brief description of what this PR does.

## Type of Change
- [ ] Bug fix (non-breaking change fixing an issue)
- [ ] New feature (non-breaking change adding functionality)
- [ ] Breaking change (fix or feature causing existing functionality to break)
- [ ] Documentation update

## Related Issues
Fixes #123
Related to #456

## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing completed

## Screenshots (if applicable)
[Add screenshots of UI changes]

## Checklist
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Comments added for complex logic
- [ ] Documentation updated
- [ ] No new warnings generated
- [ ] Tests pass locally

What Makes a Good PR?

โœ… Good PRs:

  • Focused on single feature/fix
  • Small and reviewable (< 500 lines)
  • Clear description and test plan
  • Screenshots for UI changes
  • Commits are logical units

โŒ Bad PRs:

  • Mix multiple unrelated changes
  • Massive (1000+ lines)
  • No description
  • No tests
  • Commits are random work-in-progress

If your PR is large:

  • Break into smaller PRs
  • Submit incrementally
  • Each PR should be independently valuable

Review Process

What to Expect

  1. Automated Checks

    • Tests run automatically via CI
    • Linter runs automatically
    • Must pass before review
  2. Code Review

    • Maintainer reviews within 1-3 days
    • May request changes
    • May ask questions for clarification
  3. Revision

    • Address review feedback
    • Push new commits to same branch
    • Re-request review when ready
  4. Merge

    • Maintainer merges when approved
    • Your contribution is live!

Responding to Feedback

Good responses:

  • โœ… Ask clarifying questions
  • โœ… Explain your reasoning
  • โœ… Make requested changes promptly
  • โœ… Thank reviewers for their time

Poor responses:

  • โŒ Argue without listening
  • โŒ Ignore feedback
  • โŒ Take criticism personally

Remember: Code review is about the code, not about you personally. We're all working together to make the project better!


Specific Contribution Areas

Documentation

Always welcome:

  • Fix typos or unclear explanations
  • Add examples or diagrams
  • Improve error messages
  • Update outdated information

How to contribute docs:

  1. Edit wiki pages directly (if you have access)
  2. Or submit PR with documentation changes
  3. No tests required for doc-only changes

Bug Fixes

Before fixing:

  1. Check if issue already reported
  2. Reproduce the bug locally
  3. Write test demonstrating the bug
  4. Fix the bug
  5. Verify test now passes

Bug fix PR should include:

  • Description of bug
  • Steps to reproduce
  • Root cause explanation
  • Test proving fix works

New Features

Before implementing:

  1. Open issue describing the feature
  2. Discuss approach with maintainers
  3. Get approval before starting work
  4. Break large features into smaller PRs

Feature PR should include:

  • Feature description and use case
  • Design decisions explained
  • Tests for happy path and edge cases
  • Documentation updated
  • Screenshots (for UI features)

Database Migrations

Creating migrations:

  1. Follow Migration Guide
  2. Test with fresh database
  3. Test with existing data
  4. Document in PR what the migration does

Migration PR requirements:

  • Migration follows naming convention: NNN-description.sql
  • Includes both "-- Up" and "-- Down" sections
  • Tested locally with fresh database
  • Tested locally with existing data
  • Comments explain purpose

Security

Reporting Security Issues

Do NOT open public issues for security vulnerabilities.

Instead:

  1. Email [email protected] (or maintainer directly)
  2. Include detailed description
  3. Wait for response before disclosure

We will:

  • Acknowledge within 48 hours
  • Provide fix timeline
  • Credit you in security advisory (if desired)

Security Guidelines

When contributing code:

โœ… DO:

  • Use prepared statements for all SQL queries
  • Validate all user input with Zod schemas
  • Sanitize output (Pug does this automatically)
  • Use HTTPS for external requests
  • Hash passwords with scrypt (never plain text)
  • Strip EXIF from uploaded images

โŒ DON'T:

  • Concatenate user input into SQL
  • Trust MIME types (validate magic bytes)
  • Store secrets in code or environment variables
  • Use MD5 or SHA1 for passwords
  • Expose sensitive data in logs

See: Security Overview


Common Contribution Scenarios

Fixing a Typo in Documentation

Simplest contribution:

  1. Edit wiki page directly (if you have wiki access)
  2. Or fork repo, edit file, submit PR
  3. No tests needed for typo fixes

Example PR title: docs: fix typo in Member User Guide

Adding a New Test

Great first contribution:

  1. Find code with no tests or insufficient coverage
  2. Write test following Testing Guide
  3. Submit PR

Example PR title: test: add edge case tests for species search

Fixing a Bug

Typical workflow:

  1. Reproduce: Verify bug exists
  2. Test: Write failing test
  3. Fix: Implement fix
  4. Verify: Test now passes
  5. Submit: Create PR

Example PR:

fix(submissions): handle null witness_verification_status

When witness field is null (old submissions), the status
display was throwing error. Added null check and default value.

Added test case for null witness status.

Fixes #123

Adding a Feature

More involved:

  1. Discuss: Open issue or discussion
  2. Design: Plan database changes, API, UI
  3. Implement: Write code incrementally
  4. Test: Add comprehensive tests
  5. Document: Update relevant docs
  6. Review: Address feedback
  7. Merge: Celebrate! ๐ŸŽ‰

Pull Request Best Practices

Small, Focused PRs

โœ… Good example - Single focused change:

feat(admin): add bulk approve submissions button

Adds button to approval queue allowing admins to approve
multiple submissions at once.

- Added checkbox to each submission row
- Added "Approve Selected" button
- Backend endpoint handles bulk approvals in transaction
- Email notifications sent for each approval

Changes:
- src/routes/admin.ts: bulk approve handler
- src/views/admin/queue.pug: checkboxes and button
- src/__tests__/admin.test.ts: bulk approval tests

Files changed: 3
Lines added: 156

โŒ Bad example - Multiple unrelated changes:

fix: various fixes

Fixed some bugs and added some features.

Files changed: 23
Lines added: 2,431

Descriptive Titles

โœ… Good titles:

  • feat(api): add member search endpoint with fuzzy matching
  • fix(submissions): resolve race condition in witness confirmation
  • docs(wiki): add troubleshooting guide for common errors

โŒ Bad titles:

  • update
  • fixes
  • changes to admin stuff

Clear Descriptions

Include:

  • What: What changed
  • Why: Why the change was needed
  • How: How it was implemented (for complex changes)
  • Testing: How you tested it
  • Screenshots: For UI changes

Code Review Guidelines

As a Reviewer

Provide constructive feedback:

โœ… Good:

This looks great! One suggestion: could we extract this logic
into a separate function for reusability?

Also, should we add a test case for the empty array scenario?

โŒ Bad:

This code is terrible. Rewrite it.

Focus on:

  • Correctness
  • Security
  • Performance
  • Maintainability
  • Test coverage

Be nice! Contributors are volunteering their time.

As a Contributor

Respond professionally:

โœ… Good:

Thanks for the feedback! I'll extract that into a helper function.

Good catch on the empty array case - adding that test now.

โŒ Bad:

No, my way is better.

Don't take it personally! Reviews make the code better.


Release Process

For maintainers:

  1. Merge approved PRs to main
  2. Update version in package.json (semantic versioning)
  3. Create git tag: git tag v1.2.3
  4. Push tag: git push origin v1.2.3
  5. Deploy to production
  6. Monitor for issues

Semantic versioning:

  • 1.0.0 โ†’ 1.0.1 - Bug fixes (patch)
  • 1.0.0 โ†’ 1.1.0 - New features (minor)
  • 1.0.0 โ†’ 2.0.0 - Breaking changes (major)

Getting Help

Where to Ask Questions

Before asking:

  • Search existing issues and discussions
  • Read relevant documentation
  • Try to solve it yourself first

Where to ask:

  • GitHub Discussions - General questions, ideas
  • GitHub Issues - Bug reports, feature requests
  • Pull Request Comments - Questions about specific code

How to ask good questions:

  1. Context: What are you trying to do?
  2. Problem: What's not working?
  3. Attempts: What have you tried?
  4. Environment: Dev or production? Node version?
  5. Code: Relevant code snippets or error messages

Example good question:

I'm trying to add a new field to the submission form, but when I submit
the form, the field value isn't being saved to the database.

Steps I've taken:
1. Added field to Pug template: input(name="new_field" type="text")
2. Added field to Zod schema: new_field: z.string()
3. Ran tests - they pass

Environment: Node 20.5, local development

Error: No error shown, but when I query the database, the new_field
column is NULL.

Am I missing a step in the form submission handler?

Recognition

Contributors

All contributors will be:

  • Listed in GitHub contributors
  • Mentioned in release notes (for significant contributions)
  • Credited in commit history

Types of Contributions Valued

Not just code! We appreciate:

  • ๐Ÿ“ Documentation improvements
  • ๐Ÿ› Bug reports
  • ๐Ÿ’ก Feature suggestions
  • ๐Ÿงช Test additions
  • ๐ŸŽจ UI/UX improvements
  • ๐Ÿ” Code reviews
  • โ“ Answering questions in discussions

Every contribution matters!


Related Documentation


Thank You!

Thank you for contributing to the Mulm project. Your efforts help aquarium societies worldwide manage their breeding programs and preserve species through captive propagation.

Happy coding! ๐ŸŸ๐ŸŒฑ๐Ÿชธ

โš ๏ธ **GitHub.com Fallback** โš ๏ธ