Skip to main content

Getting Started

Thank you for contributing to Gumroad! This guide will help you write high-quality code and create effective pull requests.
All contributors must follow these guidelines. Pull requests that don’t meet these standards may be rejected or require significant revisions.

Communication Standards

Use native-sounding English in all communication:
  • ❌ Bad: “HOW IS THIS GOING???”, “how’s dis going”, “thnx fr update”
  • ✅ Good: “Is this actively being worked on? I’ve started work on it here…”
Avoid:
  • Excessive capitalization
  • Multiple question marks
  • Grammatical errors
  • Typos
  • Informal abbreviations

Pull Request Guidelines

Required Elements

Every non-trivial pull request must include:
1

Clear description

Follow the PR description structure (see below)
2

Self-review comments

Comment on your own code to guide reviewers
3

Test updates

Include updates to tests, especially end-to-end tests
4

Visual documentation

For UI changes: video of before/after with light/dark mode and mobile/desktop
5

AI disclosure

Declare AI usage (see disclosure format below)

PR Description Structure

Non-trivial PRs should follow this format:

PR Size Guidelines

Break up large PRs (1000+ lines) into smaller PRs (~100 lines each). Large PRs are harder to review and more likely to introduce bugs.

Testing Requirements

Your PR must:
  • Include tests for all new features
  • Update existing tests for modified features
  • Include end-to-end tests for user-facing changes
  • Show test results passing locally
Tests must fail when the fix is reverted. If the test passes without the application code change, it is invalid.

Code Standards

General Principles

Don’t leave TODOs or placeholders. Complete the implementation in your PR.
Code should be self-documenting. Don’t leave explanatory comments. Use clear variable and method names instead.
Just fix them. Don’t add comments saying “sorry” or “I know this is bad”.
Pricing, calculations, and discount application belong in Rails, not the frontend. The frontend renders state provided by the backend. Enforce all constraints on the server.

Language Versions

Always use the latest stable versions:
  • Ruby 3.4.3
  • Rails 7.1.6
  • TypeScript 5.5.0
  • React 18.1.0
  • Node 20.17.0

Code Style

Text Formatting

Use sentence case, not title case:
  • ✅ “Save changes”
  • ❌ “Save Changes”

Constants

Assign raw numbers to named constants:

Testing Guidelines

Test Structure

Don’t start RSpec test names with “should”. See Better Specs.

Test Data

  • Use factories for test data instead of creating objects directly
  • Use @example.com for emails in tests
  • Use example.com, example.org, example.net for custom domains
  • Write descriptive test names that explain the behavior
  • Keep tests independent and isolated
  • Group related tests together

VCR Cassettes

Scope VCR cassettes to specific test files. Sharing cassettes across tests causes collisions where tests read incorrect cached responses.

Sidekiq Assertions

Sidekiq Jobs

Naming Convention

New Sidekiq job class names must end with “Job”:

Queue Selection

Queue priorities (highest to lowest):
  1. critical - Receipt/purchase emails only
  2. default - Time-sensitive background jobs
  3. low - Most background jobs (use this by default)
  4. mongo - Legacy queue for one-time scripts
Select the lowest priority queue that meets your requirements. Most jobs should use low unless time-sensitive.

Job Deduplication

For deduplication using sidekiq-unique-jobs:
Do NOT use on_conflict: :replace - it’s CPU expensive and slow because it scrolls through the Scheduled Set to find existing jobs.

Feature Development

Feature Flags

Use feature flags (Flipper) for new features:

Database Patterns

Do not use database-level foreign key constraints (add_foreign_key). This simplifies data migration and sharding operations at scale.
When creating financial records (receipts, sales), copy specific values (amount, currency, percentage) at purchase time instead of referencing mutable data like a DiscountCode ID. This ensures historical records remain accurate if the original object is edited or deleted.
Prefer re-using deprecated boolean flags instead of creating new ones. Deprecated flags are named DEPRECATED_<something>. Reset values on staging and production first, then rename.

Backfilling Data

Do NOT perform backfilling operations via ActiveRecord callbacks. Create scripts in app/services/onetime folder instead.
Why:
  • Gumroad has millions of users and products
  • Callback-based backfilling can enqueue millions of uncontrollable jobs
  • This can crash Sidekiq (Redis out of memory)
  • Creates massive replica lag

Code Organization

File Locations

Concerns

Don’t create new files in app/modules/ (legacy). Use concerns in the right directory: app/models/concerns/, app/controllers/concerns/

Path/URL methods

Don’t create methods ending in _path or _url - they might collide with Rails route helpers. Use a module like CustomDomainRouteBuilder

Public IDs

Use Nano IDs to generate external/public IDs for new models

Modern naming

Use product instead of link, buyer instead of customer, seller instead of creator in new code

Writing Issues

Issue Structure

Issues for enhancements, features, or refactors:
Keep it short. The title should carry most of the weight — the body adds context the title can’t.

Writing Bug Reports

A great bug report includes:
1

Quick summary

Brief description of the issue
2

Steps to reproduce

Be specific! Include:
  • Exact steps taken
  • Sample code if applicable
  • Environment details
3

Expected behavior

What you expected would happen
4

Actual behavior

What actually happens
5

Notes

Why you think this might be happening, or things you tried that didn’t work

Code Patterns to Avoid

  • ❌ Dynamic Tailwind class interpolation: `text-${color}`
  • ❌ Using unless conditionals
  • ❌ Creating files in app/modules/
  • ❌ Methods ending in _path or _url
  • ❌ Using $.ajax (use request instead)
  • ❌ Leaving TODO comments
  • ❌ Explanatory comments
  • ❌ Starting tests with “should”
  • ❌ Foreign key constraints
  • ❌ Backfilling via callbacks

Reasoning Over Changes

Explain the reasoning behind your changes, not just the change itself:
  • Describe the architectural decision
  • Identify the specific problem being solved
  • For bug fixes, identify the root cause
  • Don’t apply a fix without explaining how the invalid state occurred
Reviewers need to understand WHY you made these changes, not just WHAT changed.

Help Wanted

Looking for issues to contribute to? Any issue with label help wanted is open for contributions: View open issues

License

By contributing, you agree that your contributions will be licensed under the MIT License.

Next Steps

Architecture

Understand the system architecture

Testing

Write comprehensive tests

Authentication

Learn authentication patterns

Deployment

Deploy your changes