|
| 1 | +--- |
| 2 | +description: This rule explains the project's tech stack and code conventions |
| 3 | +globs: |
| 4 | +alwaysApply: true |
| 5 | +--- |
| 6 | +This rule serves as high-level documentation for how the codebase is structured. |
| 7 | + |
| 8 | +## Rules for AI |
| 9 | + |
| 10 | +- Use this file to understand how the codebase works |
| 11 | +- Treat this rule/file as your "source of truth" when making code recommendations |
| 12 | +- When creating migrations, always use `dm makemigrations` instead of creating the file yourself |
| 13 | + |
| 14 | +## Project Tech Stack |
| 15 | + |
| 16 | +- Web framework: Django |
| 17 | + - Django's built-in testing framework |
| 18 | + - Django Ninja for API development |
| 19 | + - Django Q2 for background tasks |
| 20 | + - Stimulus JS for frontend interactivity |
| 21 | + - TailwindCSS for styles |
| 22 | +- Database: PostgreSQL |
| 23 | +- Background tasks: Django Q2 |
| 24 | +- External |
| 25 | + - Payments: Stripe |
| 26 | + - [Other external services your project uses] |
| 27 | + |
| 28 | +## Project conventions |
| 29 | + |
| 30 | +These conventions should be used when writing code for the project. |
| 31 | + |
| 32 | +### Convention 1: Minimize dependencies, vanilla Django is plenty |
| 33 | + |
| 34 | +Dependencies are a natural part of building software, but we aim to minimize them when possible to keep this codebase easy to understand, maintain, and contribute to. |
| 35 | + |
| 36 | +- Push Django to its limits before adding new dependencies |
| 37 | +- When a new dependency is added, there must be a strong technical or business reason to add it |
| 38 | +- When adding dependencies, you should favor old and reliable over new and flashy |
| 39 | + |
| 40 | +### Convention 2: Leverage models and mixins over separate service layers |
| 41 | + |
| 42 | +This codebase adopts a "skinny views, fat models" convention following Django's MVT pattern. We put business logic in models and avoid separate folders for business logic. |
| 43 | + |
| 44 | +- Organize large pieces of business logic into Django models and mixins |
| 45 | +- While a mixin _may_ offer shared functionality, it can also be a "one-off" mixin that is only included in one place for better organization and readability |
| 46 | +- When mixins are used for code organization, they should be organized around the "traits" of a model; not for simply moving code to another spot in the codebase |
| 47 | +- When possible, models should answer questions about themselves—for example, we might have a method, `account.balance_series` that returns a time-series of the account's most recent balances |
| 48 | + |
| 49 | +### Convention 3: Prefer server-side solutions over client-side solutions |
| 50 | + |
| 51 | +- When possible, leverage Django templates over complex, JS-driven client-side solutions |
| 52 | +- When writing a client-side solution, use Stimulus controllers and keep it simple! |
| 53 | +- Keep client-side code for where it truly shines. For example, bulk selection is a case where server-side solutions would degrade the user experience significantly |
| 54 | + |
| 55 | +### Convention 4: Sacrifice performance, optimize for simplicitly and clarity |
| 56 | + |
| 57 | +This codebase is still young. We are still rapidly iterating on domain designs and features. Because of this, code should be optimized for simplicitly and clarity over performance. |
| 58 | + |
| 59 | +- Focus on good OOP design first, performance second |
| 60 | +- Be mindful of large performance bottlenecks, but don't sweat the small stuff |
| 61 | + |
| 62 | +### Convention 5: Prefer semantic, native HTML features |
| 63 | + |
| 64 | +The HTML spec has improved tremendously over the years and offers a ton of functionality out of the box. We prefer semantic, native HTML solutions over JS-based ones. A few examples of this include: |
| 65 | + |
| 66 | +- Using the `dialog` element for modals |
| 67 | +- Using `summary` / `details` elements for disclosures (or `popover` attribute) |
| 68 | + |
| 69 | +Stimulus works very well with these native elements and we optimize for this. |
| 70 | + |
| 71 | +### Convention 6: Use Django's testing framework and fixtures |
| 72 | + |
| 73 | +Due to the open-source nature of this project, we have chosen Django's testing framework and fixtures for testing to maximize familiarity and predictability. |
| 74 | + |
| 75 | +- Always use Django's testing framework and fixtures for testing |
| 76 | +- Keep fixtures to a minimum. Most models should have 2-3 fixtures maximum that represent the "base cases" for that model |
| 77 | +- "Edge cases" should be created on the fly, within the context of the test which it is needed |
| 78 | +- For tests that require a large number of fixture records to be created, use test helpers to act as a "factory" for creating these |
| 79 | + |
| 80 | +### Convention 7: Use Django models for complex validations, DB for simple ones, keep business logic out of DB |
| 81 | + |
| 82 | +- Enforce `null` checks, unique constraints, and other simple validations in the DB |
| 83 | +- Django model validations _may_ mirror the DB level ones, but not 100% necessary |
| 84 | +- These are for convenience when error handling in forms. Always prefer client-side form validation when possible |
| 85 | +- Complex validations and business logic should remain in Django models |
0 commit comments