Starter Based Programming


Starter Based Programming is the pattern of using dedicated Starter projects to begin all new projects. All newly created projects in Starter Based Programming are forks from existing projects. They remain forks through the course of the development.

By creating projects this way, we inherit all of the knowledge of the Starter. Forks and Starters live symbiotically with each other. Forks prevent projects from repeating past work and they generalize their solutions to contribute them back. This cycle repeats itself causing ecosystems to converge on the best practices together. With a mature collection of Starters to choose from, developers can create new applications with standardized patterns, components, structure, and velocity.

The 5 Rules

  1. Every new project should be a fork of an existing project.
  2. After we fork a `starter`, we refer to it as the `upstream`. And the new project as the `origin`.
  3. Pull updates from `upstream` into `origin` when possible. This is called "Syncing with `upstream`".
  4. Project forks are hierarchal, and updates should only flow from `upstream` to `origin`. Never push updates from `origin` directly into `upstream`.
  5. Generalize features and re-commit them into `upstream`.

Starter projects are viable even if they only compose a small amount of files and opinions. The smallest viable Starter project for JavaScript projects may simply be a `package.json`, `index.js`, and `.gitignore`. Enterprises should create their Starters for each technology stack they use so that new teams can inherit the knowledge of their predecessors.

Hierarchical Starters

Starters can be forks of other starters. For example, a `python-starter` may act as the `upstream` for `python-starter-fastapi`. The former might define the `pyproject.toml` format, the linter selection, and build tools while the latter will define the `fastapi` core routes and common utilities.


With a hierarchical Starter repository, new projects can fork at the appropriate level. One or two levels is generally enough to satisfy most new project needs. Essentially, we prefer more horizontal trees over vertical trees to make syncing easier.

Forking from a starter

You can use `git` to easily setup `origin` and `upstream` remotes.

git clone project && cd project git remote add upstream git remote set-url origin

Syncing back with upstream

Sometimes, a feature arises in a project that is not core to the project's domain. Some examples are a reusable Button component, a linter configuration, or folder convention. These are general solutions that other projects might want to use, but are not suitable to be a full-fledged library. When this is encountered, one should commit the changes to `upstream` and pull them into `origin`.

# upstream repo git commit -m "Add new lint rules"
# origin repo git pull upstream/main

This is called "Syncing with upstream."

Where do shared component libraries fit in?

Exactly where they are now. Starter projects should use the component libraries at the appropriate level of granularity.

How is this different from Project Generators like create-next-app?

Using a project generator is often the first step in creating a new Starter. It is recommended to add the output of a project generator into `git`. If one solely uses project generators, they will not inherit best practices in an easy, git-friendly way past the initial genesis. By using `create-next-app` as a base for your Starter, your projects can grow as `create-next-app` grows too.