Skip to content
Open {re}Source
04Creating

The Files That Turn a Repository Into a Project

A repository is a folder with a contract, and the contract is a handful of files most people copy from somewhere without reading. Each one answers a question a stranger would otherwise ask you in an issue. Here’s what each file is for, the smallest version that works, and where GitHub shows it.

GitHub reads the files and shows them to everyone#

GitHub looks for these files by name, in .github/, then at the root, then in docs/, and links what it finds. The About box of chalk, a terminal color library with 42 million dependents on GitHub as of September 2026, lists five of them:

The names aren’t case-sensitive. Chalk’s files are readme.md, license, code-of-conduct.md, contributing.md and .github/security.md, and GitHub finds them all. Uppercase is a convention, not a requirement.

File Answers Add it
README.md What is this, and how do I use it? Day one
LICENSE Am I allowed to use it? Day one
CODE_OF_CONDUCT.md How do people treat each other here? Before you share
CONTRIBUTING.md How do I send a change? Before you share
SECURITY.md Where do I report a vulnerability? Before you share
.github/ISSUE_TEMPLATE What do you need in a bug report? First issues
.github/FUNDING.yml Can I pay you? When you want it
CHANGELOG.md What changed, and will it break my code? First release
CODEOWNERS Who reviews what? Second maintainer

The files work on any forge. Where GitLab and Codeberg look for them is further down.

The README sells the project on one screen#

A visitor decides in one screen whether to keep reading, so that screen says what the project does and how to start. The first screen of HTTPX, an HTTP client for Python, has all of it: a logo, a one-line pitch, two badges, what it supports, the install command and a first request with its output:

The order that works for most projects:

  1. The name and one sentence on what it does, in words a newcomer searches for.
  2. What it looks like: a screenshot, a GIF of the terminal, or the output of the example.
  3. Install: one command per package manager you support.
  4. Usage in 30 seconds: the smallest example that does something real.
  5. Links: the docs, how to contribute, the license.

Two or three badges are enough. HTTPX’s say whether the tests pass and which version is out; downloads, stars and “PRs welcome” add nothing a reader needs to decide.

A README that passes the one-screen test:

Template for README.md. All templates

# Project name

One sentence: what it does and for whom.

[![CI](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/ci.yml/badge.svg)](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/ci.yml)
[![License](https://img.shields.io/github/license/YOUR_ORG/YOUR_REPO)](LICENSE)

![A screenshot or a short GIF of the project at work](docs/screenshot.png)

## Install

```sh
# the one command that installs it
```

## Usage

```sh
# the smallest example that does something useful
```

Expected output:

```text
what the reader should see
```

## Documentation

Link to the docs, or to `docs/` if they live in the repository.

## Contributing

Issues and pull requests are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) first.

## License

[MIT](LICENSE)

The common mistake: a README that starts with the history of the project. Why you built it can go in a section further down, or in a blog post. A “Non-goals” section, on the other hand, saves you replies: Saying No explains why. The reader’s side of all this is in Reading a Repository in Five Minutes.

LICENSE goes in the repository, not only in the manifest#

Without a LICENSE file, nobody has the right to use your code, whatever the README says. Put the full text at the root, and the SPDX identifier in your manifest (package.json, pyproject.toml, Cargo.toml). Choosing a License covers which one and where it goes.

It’s the one file the .github repository trick, further down, can’t provide: GitHub requires it in each repository, so that it ships with every clone and every package.

A code of conduct names who to write to#

A code of conduct is only as good as its contact line. Most projects adopt the Contributor Covenant, version 3.0 since 2025, and the template leaves a placeholder where the reporting address goes. A GitHub code search for [INSERT CONTACT METHOD], the placeholder of version 2, finds 4,608 files named CODE_OF_CONDUCT.md, as of September 28, 2026: rules nobody can enforce, because nobody can report a breach.

Use an address more than one person reads, and turn on Settings › Moderation options › Reported content, so contributors can flag a comment to you without leaving GitHub. How to Create a Code of Conduct compares the templates.

CONTRIBUTING.md is for the reply you’d otherwise retype#

Most people won’t read it, and that’s fine: it’s the link you paste when a pull request skips the rules. GitHub links it when someone opens an issue or a pull request, and that’s as close as it gets to being read first.

Chalk’s contributing.md is one sentence pointing to the code of conduct. That’s a start. What saves you the most replies:

Template for CONTRIBUTING.md. All templates

# Contributing

Thanks for taking the time. This page is the short version of how to send a change.

## Before you start

- For a bug, search the [open issues](../../issues) first. If it's new, open one with the steps to reproduce.
- For a feature or a larger change, open an issue and wait for a reply before writing code. It saves you a rejected pull request.
- Small fixes (a typo, a broken link) can go straight to a pull request.

## Set up

```sh
git clone https://github.com/YOUR_ORG/YOUR_REPO.git
cd YOUR_REPO
# install the dependencies
# run the tests
```

## Send a pull request

1. Fork the repository and create a branch from `main`.
2. Make the change. Add or update a test when behavior changes.
3. Run the tests and the linter locally.
4. Open the pull request and fill in the template.

We squash-merge, so the pull request title becomes the commit message. Write it like `fix(parser): handle empty input`.

## Review

A maintainer replies within a week. If nothing has happened after that, comment on the pull request once.

## Questions

Ask in [Discussions](../../discussions), not in issues.

## Code of conduct

Everyone taking part follows the [code of conduct](CODE_OF_CONDUCT.md).

The common mistake: setup steps nobody has run since they were written. Clone the repository into a new folder once a year and follow your own guide to the letter. Two more lines belong here when they apply: how you handle AI-assisted contributions (a policy you can copy), and what contributors agree to when they send code (CLA or DCO).

A SECURITY.md sends vulnerability reports to a private channel instead of a public issue. Chalk’s is two lines: a link to the private reporting form, and “No AI slop will be accepted”. Yours needs the versions you fix, where to report and how fast you’ll answer. Security for Maintainers has the settings that go with it.

Template for SECURITY.md. All templates

# Security policy

## Supported versions

| Version | Supported                 |
| ------- | ------------------------- |
| 2.x     | Yes                       |
| 1.x     | Security fixes until DATE |
| < 1.0   | No                        |

## Report a vulnerability

Please don't open a public issue.

Use [private vulnerability reporting](https://github.com/YOUR_ORG/YOUR_REPO/security/advisories/new), or write to security@example.org.

Include the affected version, the steps to reproduce, and what an attacker gains.

## What to expect

- We acknowledge your report within 3 working days.
- We send a first assessment within 10 working days.
- We agree on a disclosure date with you. The default is 90 days after the report, sooner once a fix is released.
- We credit you in the advisory unless you ask us not to.

Issue forms ask the questions before you have to#

An issue form asks for what your first reply would have asked: the version, the steps, what happened. It’s a YAML file in .github/ISSUE_TEMPLATE/, and GitHub turns it into a form with required fields. A bug form that works:

Template for .github/ISSUE_TEMPLATE/bug.yml. All templates

name: Bug report
description: Something doesn't work as documented.
labels: ['bug', 'needs triage']
body:
  - type: markdown
    attributes:
      value: |
        Thanks for the report. Search the [open issues](../issues) first: yours may already be there.
  - type: input
    id: version
    attributes:
      label: Version
      description: Output of `project --version`.
      placeholder: 2.4.1
    validations:
      required: true
  - type: textarea
    id: expected
    attributes:
      label: What did you expect to happen?
    validations:
      required: true
  - type: textarea
    id: actual
    attributes:
      label: What happened instead?
      description: Paste the error message or the output.
      render: shell
    validations:
      required: true
  - type: textarea
    id: reproduce
    attributes:
      label: Steps to reproduce
      description: The smallest set of steps, or a link to a repository that reproduces it.
      placeholder: |
        1. Run `…`
        2. …
    validations:
      required: true
  - type: textarea
    id: environment
    attributes:
      label: Environment
      description: Operating system, runtime version, anything that might matter.

A config.yml next to it turns off blank issues and sends questions and vulnerabilities where they belong:

Template for .github/ISSUE_TEMPLATE/config.yml. All templates

blank_issues_enabled: false
contact_links:
  - name: Question or idea
    url: https://github.com/YOUR_ORG/YOUR_REPO/discussions
    about: Ask questions and share ideas in Discussions, not in issues.
  - name: Security vulnerability
    url: https://github.com/YOUR_ORG/YOUR_REPO/security/advisories/new
    about: Report vulnerabilities privately. Don't open a public issue.

A pull request template is a Markdown file, .github/pull_request_template.md, pasted into every new pull request:

Template for .github/pull_request_template.md. All templates

## What changes, and why

<!-- One or two sentences. Link the issue: Closes #123 -->

## How to test

<!-- Commands to run, pages to open, what to look at. -->

## Checklist

- [ ] I read [CONTRIBUTING.md](../CONTRIBUTING.md)
- [ ] Tests added or updated
- [ ] Docs updated
- [ ] Breaking change (describe the migration above)

The common mistake: fifteen required fields. People fill them with “N/A” or leave for a project that asks less. Three questions get a better report than fifteen. The contributor’s side is How to Write a Bug Report That Gets Fixed.

FUNDING.yml turns on the Sponsor button#

Four lines in .github/FUNDING.yml add a Sponsor button to the repository and a list of where the money goes. Chalk’s file:

github: [sindresorhus, Qix-]
open_collective: sindresorhus
tidelift: npm/chalk
custom: https://sindresorhus.com/donate

GitHub renders it in the sidebar, one line per key:

GitHub Sponsors takes up to four people or one organization; Open Collective, Liberapay, Ko-fi, Polar, thanks.dev and a few others take one account each, and custom takes up to four links (GitHub’s docs). Package managers have their own field for the same links: Add Funding Links to Your package.json.

A changelog is written for the people upgrading#

A changelog says what changed for the people who use the project, not for the people who wrote it. A dump of the git log fails that test: “Merge branch ‘main’” tells nobody whether their code breaks. Keep a Changelog is the format most hand-written ones follow, newest version first, with an Unreleased section on top:

Template for CHANGELOG.md. All templates

# Changelog

All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/), and the project follows [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Added

- A new thing.

## [1.0.0] - YYYY-MM-DD

### Added

- First public release.

[Unreleased]: https://github.com/YOUR_ORG/YOUR_REPO/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/YOUR_ORG/YOUR_REPO/releases/tag/v1.0.0

If you publish GitHub Releases with generated notes instead, keep a CHANGELOG.md anyway, one line long, pointing to the releases page: it’s the first place people look.

Small config files end the small arguments#

A few files settle, once, what would otherwise come back in review.

  • CODEOWNERS, in .github/, the root or docs/, requests reviews from the right people automatically. The last matching line wins, so the catch-all goes first:

    Template for .github/CODEOWNERS. All templates

    # The last matching pattern wins. Owners are asked to review pull requests that touch these paths.
    
    # Default owners for everything
    *             @YOUR_ORG/maintainers
    
    # Documentation
    /docs/        @YOUR_DOCS_OWNER
    
    # CI and release automation
    /.github/     @YOUR_ORG/maintainers

    Turn on Require review from Code Owners in a branch rule to make one of their approvals mandatory.

  • .editorconfig sets indentation, line endings and the final newline for every editor that supports it.

  • .gitattributes with * text=auto eol=lf stops Windows line endings from turning a one-line change into a whole-file diff.

  • A version file pins the toolchain: .nvmrc, .python-version, rust-toolchain.toml, or .tool-versions for asdf and mise.

GitLab and Codeberg read the same files, from their own folders#

README, LICENSE, CONTRIBUTING, SECURITY and CHANGELOG are plain files: they work on every forge as they are. What changes from one forge to the next is where the templates and CODEOWNERS live, and what the forge does with them:

File GitLab Codeberg (Forgejo)
Issue templates .gitlab/issue_templates/*.md, Markdown only, no forms issue_template/ in .forgejo/, .gitea/ or .github/, with the same YAML forms and config.yml
Pull request template .gitlab/merge_request_templates/*.md pull_request_template.md in the same folders
CODEOWNERS Root, docs/ or .gitlab/, on the Premium and Ultimate tiers Root, docs/ or .forgejo/, with regular expressions (src/.* @alice), not glob patterns
FUNDING.yml No equivalent No equivalent

Forgejo also reads .github/, GitLab doesn’t. A project that moves from GitHub to Codeberg keeps its issue forms without a change; one that moves to GitLab rewrites each form as a Markdown template in .gitlab/. Source Code Hosting Platforms compares the forges themselves.

One .github repository sets the defaults for all the others#

A public repository named .github in your account or organization provides these files to every repository that has none of its own. It works for CODE_OF_CONDUCT.md, CONTRIBUTING.md, SECURITY.md, SUPPORT.md, FUNDING.yml, and issue and pull request templates (GitHub’s docs). Chalk’s organization keeps a single file in its .github repository, funding.yml, so every chalk repository gets a Sponsor button.

Two limits. A default file isn’t part of a clone or a download, so someone reading the code offline doesn’t see it. And it can’t be a license: that one lives in each repository.

GitHub’s checklist is a to-do list, not a grade#

/(owner)/(repo)/community lists the files GitHub expects and ticks the ones it found. Chalk, with every file a contributor needs, still has three open items:

A library with a narrow scope and two maintainers can live without issue templates. Your project may too. Open the page for yours, and decide item by item.

Do this now#

  • Open /(owner)/(repo)/community on your repository, and list what’s missing.
  • Read your README’s first screen as a stranger: does it say what the project does and show it?
  • Search your code of conduct for a placeholder, and put a real address in it.
  • Clone your repository into a new folder and follow CONTRIBUTING.md to the letter. Fix the first step that fails.
  • Add a SECURITY.md that points to private vulnerability reporting.
  • Add one issue form, for bugs, with three required fields.

Go further#