Defining the scope
There's a line you're going to draw whether you deliberately choose to or not. You probably drew it when you extracted this from another app into its own, or created it to fill a gap in one of your projects.
With or without Django?
Occassinoally otherwise helpful functionality is packaged as "Django Such and Such" for use in Django projects, but without having any necessary dependence on Django. This probably sounds untroubling, and indeed there's nothing awful about this, however the first scoping question you need to ask when creating a standalone app is "Does this require Django?".
Why ensure this separation? Well for starters, if you're going to be sharing this with the rest of the world, and the core functionality doesn't actually depend on Django, then you've broadened the audience.
You're also reducing yet another dependency in your package, which, even if you're using it in Django projects, is another line of dependencies that can break. Where it makes sense, reference the standard library instead of Django utilities. If something moves in a new Django version, you're now insulated from that change.
It's worth keeping in mind that you can add functionality to Django projects without Django-specific modules, or without necessarily requiring Django.
And lastly, it's often easier to test.
In a later chapter on mixed dependency support we'll examine how to separate out what's Django specific and what's not.
Choosing your dependencies
The great thing about packages like the one you're creating is that they give you funcitonality for free - maybe not free, but without the cost of writing the code and figuring out the edge cases yourself.
These benefits presume that the dependencies you're using are adequately tested and work as advertised.
Each dependency you add increases the surface area that you need to test as well opportunties for broken interactions. This is true in a Django project (site) and its equally true in your own standalone app.
Now, it's certainly unwise to rewrite everything yourself! But give thought to whether you really need to use a certain dependency or class it provides.
Among the guiding questions you should ask:
- Does the dependency provide required functionality for your app?
- Is it up to date with the Django version(s) you will be supporting?
- Does it have tests that cover its core functionality?
- Does it have documentation? This could be complete documentation on Read the Docs or even a fully fleshed out README, depending on the scope of the app.
This is a "mistake" I've made. An example of this in my own case is a decision to use the otherwise fantastic django-extensions app as a dependency for my own, django-organizations. I wanted a timestamped models - a good thing to have which you'll notice when its missing - and moreover I wanted slug fields that took care of themselves. For this I wanted the AutoslugField. This wasn't a bad decision so much as a restrictive one. I could've just used a typical Django slug field but for my own needs the Autoslug was where it was at. Later I realized that some people, including myself, might need to be able to configure how slugs are made, and this shouldnt' be so fixed.
Specific vs. Generalized
It's a common temptation to write reusable code and to start making it as generalized as possible. This is almost always a mistake, regardless of how useful the more general case is. You're likely to spend time anticipating non-issues and failing to anticipate actual needs.
For example you might have a subscription management library for SaaS apps which is built with Stripe. A more generalizable approach would account for different subscription and payment backends. However unless you actively make uses of these different scenarios yourself trying to handle them all is likely to lead to half-way solutions. And creating a more general system capable of handling user-customized scenarios when you start out with your app will cause you to sepnd more time on abstractions that could be better spent getting your app polished.