Some Thoughts About Open Source "Opinionated" Extensions
I was thinking about the difference between opinionated and non-opinionated packages, and what principles might guide the design of opinionated extensions like this one so they don't become so narrow that they're unusable.
1. Opinionated vs. Non-Opinionated Design
Understanding Opinionated Packages:
- Enforce specific workflows based on strong conventions
- Provide prescriptive defaults (like
daisyUI
in powercrud) - Make integrated technology choices (eg HTMX, Crispy Forms)
- Reduce flexibility but improve consistency and developer experience
- Follow convention over configuration
- Guide users toward best practices with sensible defaults
Understanding Non-Opinionated Packages:
- Provide flexible building blocks with minimal assumptions
- Are highly configurable with few defaults
- Remain neutral to technology choices
- Require users to make more decisions but support broader use cases
- Focus on core functionality without aesthetic/UX decisions
Positioning The Package:
- Clearly state in documentation whether your package is opinionated
- Provide escape hatches for users who need customization
- Consider progressive enhancement where opinionated features can be disabled
2. Architecture for Extensions
Respect the Base Package:
- Extend rather than replace (like powercrud uses mixins with neapolitan)
- Maintain compatibility with the parent package's API
- Follow Django's "explicit is better than implicit" philosophy
Progressive Enhancement:
- Features gracefully degrade (like HTMX features only activating if HTMX is available)
- Provide sensible fallbacks (like using form_class if create_form_class isn't specified)
- Layer opinionated features so they can be individually adopted or discarded
Maintainability & Extensibility:
- Keep core functionality minimal, with hooks or APIs to allow extension
- Provide clear extension points through well-documented mixins and hooks
- Allow overriding through configuration
3. API & Integration Design
Consistent API:
- Follow the conventions of the ecosystem (Django-style settings, class-based APIs)
- Ensure naming is predictable and meaningful
- Avoid surprising behavior (e.g., modifying global state without warning)
Compatibility & Integration:
- Support the latest stable versions of dependencies while maintaining backwards compatibility
- Make integration points clear—where can users plug in their own logic?
- If overriding framework behavior, be explicit about it
4. Documentation & Error Handling
Clear Documentation:
- Document your opinions explicitly
- Provide a "Why?" section explaining the package's purpose and design decisions
- Include quickstart guides and real-world examples
- Show how to override defaults when needed
Error Handling & Debugging:
- Provide helpful error messages with actionable suggestions
- Log warnings instead of failing silently
- Consider exposing debug tools or settings for advanced users
5. Finding the Right Balance
- Make opinions clear in documentation
- Provide escape hatches for those who need something different
- Consider isolating opinions into separate components/modules
- Understand your target audience and their common needs
- Make sure opinions solve real problems and reduce boilerplate