This is an in-progress draft, enjoy.

Scoping the extraction and drawing boundaries

The first step in developing a reusable app from your existing project code is drawing the boundaries for what will go into the new app and what needs to stay or come out. You may already have a highly isolated app or you may have an app that's still tightly coupled to the rest of your project. You'll need to decide now where the interface boundaries are going to be and what the replacements will look like if you need to pull tightly integrated code out.

It helps to define the job this new reusable app will have. What will it do? What won't it do? What is core to its job and what should be left to the developer-as-user to configure?

You'll want to consider:

  • "Business goal" of the app
  • Views
  • Template tags
  • Middleware
  • Forms
  • Models
  • Signals
  • Tasks
  • Templates
  • Circular relationships in your project code
  • Multiple apps
  • Namespacing

When you start scoping out the new app to be extracted, you need to look at two different kinds of scope: obviously what code need to come out, that is classes and functions, but first what jobs these components have.

The dimensions of reusable apps

With that in mind, let's consider that there are essentially two dimensions you can use to split up your code:

  • problem domain (think "business domain")
  • functionality (domain agnostic)

A domain solution is one that solves a specific problem that could be defined as a business problem. This broadly extends to apps as diverse as payments (dj-stripe) and content management systems (django-cms, wagtail).

A functional solution is one that solves a problem inherent to the building of Django sites regardless of their problem domain. A great example is django-extensions which is a collection of utilities which benefit any Django site.

If you add functionality that solves a domain problem to a Django site, that's something your end users should notice. If you add functionality that solves a functional problem that's not something your end users should directly notice.

To the greatest degree possible you want to make sure you understand which dimension you want to use to define your app.

What does it do

Put yourself in the shoes of a marketer for a moment (no, really). How would you describe your app? What problem does it solve? How does it solve that problem? How does it solve this problem for many other applications?

The answers to these questions will be helpful in marketing your open source package - if an open source package is your goal - but that's not why we're asking them.

If in defining your new app's job you find yourself using the word "and" too much then it's quite likely the scope of your app is too broad.

How does your code use this

  • Where are there dependencies? What do they look like, that is, directionally?
  • Are they necessarily like that?
  • Can dependencies (not import but directional code) be substituted using something like signals or dependency injection?

This is the starting point for most reusable apps. If you're on your second use for your app, it's already in the rinse repeat cycle.

  • you may need to change the app - remove stuff, make two apps
  • remove proprietary things
  • identify the work that there'll be for you

Can it handle agnosticism?

The usefulness of an app is related to its agnosticism about user choices.

On one level: Is your app a Stripe subscriptions app, or is it a subscrptions app that can speak to Stripe?

On another: if it's a Stripe subscription app, can it play nicely with the end user's account models, or must it absolutely connect to the one you specify?

On the other hand, you can't handle every possible scenario, and if you try to handle anything and everything you're going to be left with a limp mess of an usable app. Is everything configurable? Yikes.

When an app is too small

There's a nebulous critical mass that an app should reach before it's released and maintained as a standalone app. First of course, if we're drawing a distinction around Django apps is that it should provide some kind of necessarily installable feature like models or template tags.

When an app is too big

It's easier to add features into a program than to remove them, and that goes double for library code like standalone apps.

When should you start thinking about extracting?

This is a subjective question to which I don't have a concrete answer for you, but I tend to like this probabilitistic reasoning:

If I do something twice there's a pretty good chance I'm going to have to do it again.

If you're building functionality into a project for a second time - or certainly beyond that - then you almost certainly have a justification for extracting.

It's worth noting that even in this case the answer may not be extracting into a reusable app. If each time you reuse the code you make a lot of changes, even little ones, you may end up creating a monstrosity of configuration checks just to make a usuable app. The key here is if you're really repeating the same thing or the similar thing you're creating is approaching a stable set of features and code.