POST

Starter Based Programming

zeevo

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.

python-starter
python-starter-fastapi
python-starter-click
python-starter-rest

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 git@github.com:user/starter.git project && cd project
git remote add upstream git@github.com:user/starter.git
git remote set-url origin git@github.com:user/project.git
origin
upstream
project/
github.com:user/project.git
github.com:user/starter.git

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.