Please see the main Antiwork Contributing Guidelines for development guidelines.
Generally: Include an AI disclosure, self-review (comment) on your code, break up big 1k+ line PRs into smaller PRs (100 loc), and include video of before/after with light/dark mode and mobile/desktop experiences represented. And include e2e tests!
- Don't use "should" in test descriptions
- Write descriptive test names that explain the behavior being tested
- Group related tests together
- Keep tests independent and isolated
- For API endpoints, test response status, format, and content
- Use factories for test data instead of creating objects directly
- Tests must fail when the fix is reverted. If the test passes without the application code change, it is invalid.
- Scope VCR cassettes to specific test files. Sharing cassettes across tests causes collisions where tests read incorrect cached responses.
- Update documentation if you're changing behavior
- Add or update tests for your changes
- Provide before & after screenshots/videos for UI changes
- Include screenshots of your test suite passing locally
- Don't comment on the parent issue when opening a PR; instead, link to the issue in your PR description.
- Use native-sounding English in all communication with no excessive capitalization (e.g HOW IS THIS GOING), multiple question marks (how's this going???), grammatical errors (how's dis going), or typos (thnx fr update).
- ❌ Before: "is this still open ?? I am happy to work on it ??"
- ✅ After: "Is this actively being worked on? I've started work on it here…"
- Explain the reasoning behind your change, not just the change itself. Describe the architectural decision or the specific problem being solved.
- For bug fixes, identify the root cause. Don't apply a fix without explaining how the invalid state occurred.
- Make sure all tests pass
- Request a review from maintainers
- After reviews begin, avoid force-pushing to your branch
- Force-pushing rewrites history and makes review threads hard to follow
- Don't worry about messy commits - we squash everything when merging to main
- The PR will be merged once you have the sign-off of at least one other developer
- Follow the existing code patterns
- Use clear, descriptive variable names
- Always use the latest version of Ruby, Rails, TypeScript, and React
- Sentence case headers and buttons and stuff, not title case
- Always write the code
- Don't leave comments in the code
- No explanatory comments please
- Don't apologize for errors, fix them
- Business logic (pricing, calculations, discount application) belongs in Rails, not the frontend. The frontend renders state provided by the backend. Enforce all constraints on the server.
- Assign raw numbers to named constants (e.g.,
MAX_CHARACTER_LIMITinstead of500) to clarify their purpose. - Avoid abstracting code into shared components if the duplication is coincidental. If two interfaces look similar but serve different purposes (e.g., Checkout vs. Settings), keep them separate to allow independent evolution.
- The Sidekiq queue names in decreasing order of priority are
critical,default,low, andmongo. When creating a Sidekiq job select the lowest priority queue you think the job would be ok running in. Most queue latencies are good enough for background jobs. Unless the job is time-sensitivelowis a good choice otherwise usedefault. Thecriticalqueue is reserved for receipt/purchase emails and you will almost never need to use it.mongois sort of legacy and we only use it for one-time scripts/bulk migrations/internal tooling. - New Sidekiq job class names should end with "Job". For example
ProcessBacklogJob,CalculateProfitJob, etc. - If you want to deduplicate a job (using sidekiq-unique-jobs), 99% of the time, you're looking for
lock: :until_executed. It is fast because it works by maintaining a Redis Set of job digests: If a job digest is in this list (O(1)), runningperform_asyncwill be a noop and will returnnil. - Furthermore, you likely should NOT use
on_conflict: :replace, because for it to remove an existing enqueued job, it needs to find it first, by scrolling through the Scheduled Set, which is CPU expensive and slow. It also means thatperform_asyncwill be as slow as the length of the queue, or fail entirely ⇒ you can break Sidekiq but just having one job like this enqueued too often.
- When creating financial records (receipts, sales), copy the specific values (amount, currency, percentage) at the time of purchase instead of referencing mutable data like a
DiscountCodeID. This ensures historical records remain accurate if the original object is edited or deleted. - Do not use database-level foreign key constraints (
add_foreign_key). Avoiding hard constraints simplifies data migration and sharding operations at scale. - Do not use dynamic string interpolation for Tailwind class names (e.g.,
`text-${color}`). Tailwind scanners cannot detect these during build. Use full class names or a lookup map. - Prefer re-using deprecated boolean flags (https://github.com/pboling/flag_shih_tzu) instead of creating new ones. Deprecated flags are named
DEPRECATED_<something>. To re-use this flag you'll first need to reset the values for it on staging and production and then rename the flag to the new name. You can reset the flag like this:# flag to reset - `Link.DEPRECATED_stream_only` Link.where(Link.DEPRECATED_stream_only_condition).find_in_batches do |batch| ReplicaLagWatcher.watch puts batch.first.id Link.where(id: batch.map(&:id)).update_all(Link.set_flag_sql(:DEPRECATED_stream_only, false)) end
- Use
productinstead oflinkin new code (in variable names, column names, comments, etc.) - Use
requestinstead of$.ajaxin new code - Use
buyerandsellerwhen naming variables instead ofcustomerandcreator - Avoid
unless - Don't create new files in
app/modules/as it is a legacy location. Prefer creating concerns in the right directory instead (eg:app/controllers/concerns/,app/models/concerns/, etc.) - Do not create methods ending in
_pathor_url. They might cause collisions with rails generated named route helpers in the future. Instead, use a module similar toCustomDomainRouteBuilder - Use Nano IDs to generate external/public IDs for new models.
- Don't start Rspec test names with "should". See https://www.betterspecs.org/#should
- Use
@example.comfor emails in tests - Use
example.com,example.org, andexample.netas custom domains or request hosts in tests. - Avoid
to_not have_enqueued_sidekiq_jobornot_to have_enqueued_sidekiq_jobbecause they're prone to false positives. Make assertions onSidekiqWorkerName.jobs.sizeinstead.
- Use feature flags for new features
- Do not perform "backfilling" type of operations via ActiveRecord callbacks, whether you're enqueuing a job or not to create missing values. Use a Onetime task instead.
- This is because we have a lot of users, products, and data.
- Example: If you enqueue a backfilling job for each user upon them being updated, it's likely going to result in enqueuing millions of jobs in an uncontrollable way, potentially crashing Sidekiq (redis would be out of memory), and/or clogging the queues because each of these jobs takes "a few seconds" (= way too slow) and/or create massive uncontrollable replica lag, etc.
- Use scripts in the
app/services/onetimefolder
A great bug report includes:
- A quick summary and/or background
- Steps to reproduce
- Be specific!
- Give sample code if you can
- What you expected would happen
- What actually happens
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
We use the Conventional Commits specification.
A commit message should be structured as follows:
type(scope): title
descriptionWhere type can be:
feat: new feature or enhancementfix: bug fixesdocs: documentation-only changestest: test-only changesrefactor: code improvements without behaviour changeschore: maintenance/anything else
Example:
feat(cli): Add mobile testing support
- Check existing discussions/issues/PRs before creating new ones
- Start a discussion for questions or ideas
- Open an issue for bugs or problems
- Any issue with label
help wantedis open for contributions - view open issues
By contributing, you agree that your contributions will be licensed under the MIT License.