Standalone lab

Build Your First GitHub Copilot Project

A guided build of a small, tested Python CLI using GitHub Copilot — writing the instruction file first, then letting Copilot work inside it, and verifying every step with commands you run yourself.

  • Beginner
  • Medium lab
  • 5 min read
Written by
The Copilot Stack Editorial Team
Published
Updated
Last technically verified

What you will be able to do

  • Write a repository instruction file before writing code, and observe what changes in Copilot's output
  • Use Copilot to implement a function against a specification you wrote first
  • Generate tests with Copilot and identify the cases it did not think to cover
  • Verify the result with commands rather than by reading the diff

Before you start

  • Python 3.11 or later on your machine
  • An editor with GitHub Copilot enabled, on any plan including Copilot Free
  • A terminal, and comfort running a command in it

Preparation from the Academy: How to Get Started with GitHub Copilot, How to Create a copilot-instructions.md File, GitHub Copilot for Python

/labs/first-copilot-project/

Most first projects with Copilot go the same way: you open an empty file, type a comment, accept what appears, and end up with something that runs. This lab does it in the other order — specification first, instruction file second, code third, and a verification step that can actually fail.

You will build a command-line tool that splits a bill between people, including the awkward part: the remainder when the total does not divide evenly.

That problem is chosen deliberately. It is small enough to finish, and it has a correct answer that a plausible-looking implementation gets wrong.

What you are building

A single command:

$ python -m billsplit 100.00 3
Each person pays: 33.34, 33.33, 33.33
Total: 100.00

Three constraints make it non-trivial:

  • The parts must sum exactly to the total. No lost or invented pennies.
  • Money is decimal, not floating point.
  • The remainder is distributed one cent at a time, not dumped on one person.

Step 1 — Create the project

mkdir billsplit && cd billsplit
python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install pytest
mkdir billsplit tests
touch billsplit/__init__.py

Step 2 — Write the instruction file first

This is the step people skip, and it is the one that changes the output.

Create .github/copilot-instructions.md:

# Copilot instructions

This is a small Python 3.11+ command-line tool.

## Money

- Money is `Decimal`, never `float`. A float total is wrong before it is slow.
- Never distribute a remainder by rounding each share independently. Compute the
  total in the smallest unit, divide, and hand out the remainder one unit at a
  time.
- The parts of a split must sum exactly to the input total. This is testable and
  must be tested.

## Style

- Type-annotate every signature including the return type.
- `from __future__ import annotations` at the top of every module.
- Raise `ValueError` with a message naming the bad input; never return `None`
  to signal an error.

## Tests

- pytest. Test the cases a plausible implementation gets wrong: totals that do
  not divide evenly, one person, zero amounts, and negative input.
- Assert exact `Decimal` values, never approximate comparisons.

Step 3 — Write the specification, then ask for the code

Create billsplit/split.py containing only this:

"""Split a total into N parts that sum exactly to the total."""

from __future__ import annotations

from decimal import Decimal


def split(total: Decimal, people: int) -> list[Decimal]:
    """Split `total` between `people`, distributing any remainder one cent
    at a time from the first share onward.

    Raises ValueError if people < 1 or total is negative or not finite.
    """

Now ask Copilot to fill it in:

Copilot prompt

Implement the split function according to its docstring and this repository’s instruction file. Work in whole cents internally. Do not round each share independently.

You should get something close to: convert to cents with int(total * 100), use divmod by people, then add one cent to the first remainder shares.

Step 4 — Add the command-line entry point

Create billsplit/__main__.py:

Copilot prompt

Add a main.py that reads a total and a person count from sys.argv, calls split, and prints the shares comma-separated followed by the total. Format every amount to exactly two decimal places. Exit with status 2 and a message on stderr for bad input.

Exit status 2 for a usage error is the convention the shell expects. A tool that exits 0 after failing is a tool that cannot be scripted.

Step 5 — Generate tests, then find what is missing

Copilot prompt

Write pytest tests for split() covering the cases named in the instruction file.

Now read them and ask what is not there. In our runs, Copilot reliably covers the even split and the uneven split, and reliably omits at least one of:

  • people = 1 — the whole total goes to one person
  • a total of 0.00
  • a negative total
  • a total with more than two decimal places, such as 10.005

That last one is the interesting case, because there is no obviously correct answer and the docstring does not specify one. Decide what it should do, write that into the instruction file, and only then ask for the test.

Validation

Run these. All three must pass before you consider the lab done.

1. The tests pass:

pytest -q

2. The parts always sum to the total. This is the property that matters, so check it exhaustively rather than trusting four examples:

python - <<'PY'
from decimal import Decimal
from billsplit.split import split

for cents in range(0, 2000):
    total = Decimal(cents) / 100
    for people in range(1, 13):
        parts = split(total, people)
        assert len(parts) == people, (total, people, parts)
        assert sum(parts) == total, (total, people, parts, sum(parts))
print("24,000 splits checked — every one sums exactly.")
PY

3. The command behaves:

python -m billsplit 100.00 3      # 33.34, 33.33, 33.33
python -m billsplit 10.00 4       # 2.50, 2.50, 2.50, 2.50
python -m billsplit 0.01 3        # 0.01, 0.00, 0.00
python -m billsplit 10.00 0 ; echo "exit=$?"   # error, exit=2

If step 2 raises an AssertionError, you have found a real bug — and you found it with a command rather than by reading the code, which is the point.

Troubleshooting

Copilot ignores the instruction file. Almost always a path problem. The file must be at .github/copilot-instructions.md relative to the workspace root your editor has open. Opening the parent directory silently breaks it.

sum(parts) != total for some inputs. The implementation is rounding each share instead of distributing whole cents. Ask Copilot to rewrite it working in integer cents throughout, and re-run the exhaustive check.

Decimal(total * 100) gives something like 1000.0000000000001. The value reached Decimal as a float. Construct from a string: Decimal("10.00"), and parse sys.argv with Decimal(arg) directly rather than Decimal(float(arg)).

python -m billsplit says “No module named billsplit”. You are not in the project root, or billsplit/__init__.py is missing.

Security considerations

Nothing here reaches a network, reads a credential or writes outside the project directory. Two habits worth starting now anyway:

  • Add .venv/ and __pycache__/ to .gitignore before your first commit.
  • Never paste real customer data into a prompt to “make the example realistic”. Prompt content leaves your machine.

Cleanup

Nothing was provisioned and nothing costs money. To remove the lab entirely:

deactivate
cd .. && rm -rf billsplit

What you should take away

The instruction file did the work. The same prompts, in a repository without .github/copilot-instructions.md, produce float arithmetic and independently rounded shares — code that looks right, passes a casual reading, and loses a penny on roughly one split in three.

The second habit is the validation step. “It should work” is not a check. Twenty-four thousand assertions is.

Sources

Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.

Primary sources

All labs