Start With the DocType Design
The most expensive mistakes in Frappe apps happen at the DocType design stage. Get the data model wrong and you're fighting the framework for the entire project. Key principles:
- Use child tables (Table fields) for repeating data — don't create linked doctypes unless the child needs its own lifecycle
- Define autoname early — changing it after data is in production requires a migration
- Use Link fields with options pointing to the correct DocType — don't use Data fields for IDs
Fixtures vs Patches
Fixtures are for reference data that should be identical across all sites (Custom Fields, Property Setters, Print Formats, Roles). Patches are for seed data that should only run once.
A common mistake is putting seed data in fixtures. Fixtures are exported and re-imported on every bench migrate — if a user modifies a fixture record, the next migrate overwrites their changes.
Client Scripts: Keep Them Thin
Client Scripts are hard to test and debug. Keep business logic on the server side (Python). Client scripts should only handle UX concerns: show/hide fields, set filters on Link fields, validate before save with clear user messages. If your client script is over 100 lines, refactor the logic to a server-side method.
Patch Best Practices
Always check existence before inserting. Always use ignore_if_duplicate. Always commit at the end. Never run patches that modify records without making them idempotent — if the patch runs twice, the result should be the same.