Embercode
✕
Learning with AILesson 16 of 16

45 min read

Choosing your next small project

Why this matters

Most unfinished beginner projects were not too hard. They were too big. The idea started as a small program and grew as the work went on: one more screen, one more setting, one more thing to save. After a month the project has no end, so it is never finished, and the person does not learn the thing they wanted to learn.

This lesson gives a method for choosing a project and fitting it into a size you can finish. The method has four parts: describe the project in one sentence, cut it to three features that a test can check, estimate the hours for each feature, and set a stop line that tells you when to stop.

The ladder of project sizes, from a calculator to a web app, is in 01-how-to-learn-programming. This lesson does not repeat that list. It assumes you have picked a size and now need to scope one project inside it. What to show of the finished project, and how to write it up, is in 12-keeping-a-project-portfolio.

Words used in this lesson

  • Feature: one thing the program does, which you can check with an input and an expected output.
  • Scope: the full set of features a project has.
  • Scope creep: the growth of scope during the work, usually one small addition at a time.
  • Estimate: a guess, written as a number of hours, for how long a feature will take.
  • Padding: a multiplier applied to the total estimate, to cover the parts you did not think of.
  • Stop line: a limit you set in advance, measured in hours, sessions or a date. When you reach it, you finish, cut, or make one decision to extend.
  • Later list: a plain list of ideas you are not doing now. It keeps good ideas from getting into the current project.

The idea, built step by step

Step 1: describe the project in one sentence

Write one sentence that says who uses the program, what goes in, and what comes out. The user can be you.

  • Too big: "A note app with sync, tags, search and a nice design."
  • Usable: "A small program that reads a text file and tells me how many words and lines it has, and which three words appear most."

The second version names an input (a text file) and an output (numbers and words). You can test that. The first version has five features hidden inside it, and two of them ("nice design" and "sync") have no clear test.

Step 2: write features that a test can check

A feature is something the program does, with an input and an output you can check by looking. Words like "fast", "nice", "clean" and "smart" cannot be checked. Replace them with something you can observe.

Here is the word-counting project written as features:

  1. The program prints the total number of words in a file.
  2. The program prints the number of lines in a file.
  3. The program prints the three most common words, ignoring capital letters and punctuation.

Each one has an input (a file) and an output you can check. Here is a small program that does all three. Save it as wordcount.py:

import re
import sys
from collections import Counter
 
path = sys.argv[1]
with open(path, encoding="utf-8") as f:
    text = f.read()
 
lines = text.splitlines()
words = text.split()
 
print(f"lines: {len(lines)}")
print(f"words: {len(words)}")
 
cleaned = re.findall(r"[a-z']+", text.lower())
top = Counter(cleaned).most_common(3)
print("top 3:")
for word, count in top:
    print(f"  {word} {count}")

sys.argv[1] is the file name you type after the program name. In python3 wordcount.py notes.txt, it is notes.txt.

Save this three-line text as notes.txt in the same folder:

The loop runs once for each item.
The loop stops when the list ends, and the loop has no end on its own.
A loop that never stops is a bug, the loop is the bug.

Run the program:

python3 wordcount.py notes.txt

The output is:

lines: 3
words: 36
top 3:
  the 6
  loop 5
  stops 2

Check each feature by hand. There are 3 lines in the file, so feature 2 is right. For feature 1, count the pieces separated by spaces: the first line has 7, the second has 16, and the third has 13. That makes 36, so feature 1 is right. For feature 3, count "the" in lower case, with no punctuation: one in the first line, three in the second, and two in the third, which is six. "loop" appears once in the first line, twice in the second and twice in the third, which is five. Both match the output.

A feature is done when you can run it and check the output against a count you made yourself. Hand counting is slow, so keep the test file short. Three lines is enough to catch most mistakes.

Feature 3 shows the difference between a feature and its details. The program uses a pattern, [a-z']+, to pick out words made of letters and apostrophes. That pattern is a detail. You can change it later without changing the feature, as long as the output still ignores capitals and punctuation. The feature is the behaviour you check, not the code that produces it.

Step 3: cut to three

Most people start with a long list of ideas. Write them all down, then mark the ones the project cannot work without. Keep three. Move the rest to the later list.

Here is the list for the word counter, before cutting:

  • total word count
  • line count
  • top 3 words, ignoring case and punctuation
  • top 10 words
  • count letters as well as words
  • read PDF and Word files
  • show a window instead of printing
  • save results to a file
  • colour the output
  • count emoji separately
  • let the user choose the file from a menu

Eleven ideas. Three are the project. The rest are good, but none is needed for the program to do its job. Read the cut list: "top 10 words" is the top-3 feature with a bigger number, so it is a change of value, not a new feature. Put it on the later list and do not build it now.

The later list is not a trash can. It is where the good ideas wait. Ideas on it do not disappear, and you can read them again when you pick the next project.

A useful question for each idea: "Would the program fail its job without this?" If the answer is no, the idea goes to the later list.

Step 4: estimate each feature in hours

For each feature, write a guess in hours of focused work. Include writing the code, running it, reading errors and fixing them. Do not include the time you would spend if the work were going well, because that is never how the first project goes.

Write the guesses on paper before you start, so you can compare them with the real time later. A guess you never wrote down cannot teach you anything.

Here are the guesses for the three word-counter features:

  • total word count: 1.0 hour
  • line count: 0.5 hours
  • top 3 words, ignoring case and punctuation: 2.0 hours

The total guess is 3.5 hours. Then multiply by a padding factor. Padding covers the parts you did not think of: reading an error message, looking up a function, a file path that is wrong, a test that fails for a reason you did not expect. A padding factor of 1.5 is a common starting point for a beginner. It is a rule of thumb, not a measurement. After your first project, compare the guess with the real time and change the factor to match your own pattern.

The file estimate.py does the arithmetic. Save it in the same folder:

features = [
    ("total word count", 1.0),
    ("line count", 0.5),
    ("top 3 words, ignore case and punctuation", 2.0),
]
 
PADDING = 1.5  # multiply the guess by this for unknowns
 
guess = sum(hours for _, hours in features)
padded = guess * PADDING
stop_line = 6.0  # hours
 
print(f"guess: {guess:.1f} h")
print(f"padded: {padded:.2f} h")
print(f"stop line: {stop_line:.1f} h")
print("fits" if padded <= stop_line else "does not fit, cut a feature")

Run it:

python3 estimate.py

The output is:

guess: 3.5 h
padded: 5.25 h
stop line: 6.0 h
fits

The padded total is 5.25 hours, which is below the stop line of 6 hours. So the plan fits. If the output had said "does not fit", the next step would be to cut a feature, not to ask for more time.

Estimate in hours of focused work, not in days. A day at a desk is a fuzzy amount. Two focused hours is a number you can check against the clock. The advice on how many hours you have each week is in 05-building-a-study-routine.

Step 5: set the stop line

The stop line is what keeps the plan finite. It is a limit you set before you start. It can be a number of hours (the example uses 6), a number of sessions (for example, four sessions of 90 minutes), or a date.

At the stop line, you do one of three things:

  1. Ship what works. Check each feature with its test. Keep the features that pass. Write down the ones that do not, as known gaps.
  2. Cut. Remove a feature that is not working and move it to the later list.
  3. Extend once, by a fixed amount, written down. For example, "one more hour, then stop no matter what". Extend only once. A second extension is the start of scope creep.

The stop line is a decision point, not a promise to keep working until the project is perfect. Its purpose is to make the decision while you are still calm, rather than at 23:00 with a broken program and a feeling that you should keep going.

Write the stop line at the top of the plan, where you will see it. A plan file for the word counter could look like this:

Project: word counter (wordcount.py)
One sentence: reads a text file, prints the word count, line count, and top 3 words.
Features:
  1. total word count (1.0 h)
  2. line count (0.5 h)
  3. top 3 words, ignore case and punctuation (2.0 h)
Padding: 1.5, padded estimate 5.25 h
Stop line: 6 hours of focused work
At stop line: ship what passes, cut the rest, one extension of one hour at most
Later list: top 10 words, letter count, PDF files, save to file, colour output

The plan fits on one screen. If it does not fit, the project is too big.

Worked example 2: a habit tracker, with a cut

A learner wants a small program that records whether they practised each day and shows their streak, the number of days in a row. The first draft of the features looks like this:

  1. Add today's entry (yes or no) to a file that keeps all past entries.
  2. Show the current streak.
  3. Show how many days in the last 30 were practice days.

Estimates, written before starting:

  • add today's entry: 2.0 hours, because the learner has not yet written to a file
  • show the current streak: 3.0 hours, because counting dates and handling missed days have several edge cases
  • show the last 30 days: 2.0 hours

Total guess: 7.0 hours. Padded at 1.5, that is 10.5 hours. The stop line is 6 hours, so the plan does not fit. The learner must cut.

The cut has to be made by a rule, not by feeling. Here the rule is: keep the features the program cannot work without. Feature 1 is the core, because without it there is no data. Feature 2 is the point of the program. Feature 3 is a summary of the same data and can wait. The new plan has two features:

  • add today's entry: 2.0 hours
  • show the current streak: 3.0 hours

Guess 5.0 hours, padded 7.5 hours. Still above 6. So the learner decides to make feature 2 simpler: show the streak only for the last 7 days, which removes the hardest date-handling case, and estimate it at 2.0 hours. Now the guess is 4.0 hours, padded 6.0 hours. That equals the stop line, so the plan fits, but with no room to spare. The learner writes that down.

This example shows two things. First, a feature that looks ordinary (a streak) can be the largest estimate, because it has hidden rules: what counts as a missed day, what happens at midnight, what happens if the file is empty. Second, a plan that does not fit is not a failure. It means the scope has to change before work starts, which is cheaper than changing it halfway through.

Worked example 3: a bill splitter, with a hidden detail

A group of three people wants a program that splits a bill evenly and prints what each person pays, in dollars and cents. The features:

  1. Split a total equally among three people.
  2. Handle cents correctly, so the shares add up to the total.
  3. Print each person's share on its own line.

Feature 1 looks easy. Feature 2 is where the hidden work is. Splitting 100.00 three ways gives 33.333 dollars each. Rounding each person's share to cents gives 33.33 three times, which adds up to 99.99. One cent is missing. The fix is a rule: the first person pays the leftover cent. The rule must be written in a test, because a program that drops a cent will look right at first glance.

Here is a quick check of that rule, run in Python:

python3 -I -c "
total = 100.00
share = round(total / 3, 2)
people = [share, share, share]
left = round(total - sum(people), 2)
people[0] = round(people[0] + left, 2)
print(people, round(sum(people), 2))
"

The output is:

[33.34, 33.33, 33.33] 100.0

The shares add up to 100.0. That check is the test for feature 2. It is short, and it is the part of the project most likely to hide a bug, so it deserves its own line in the estimate.

Estimates: feature 1 at 0.5 hours, feature 2 at 2.5 hours because of the rounding rule, feature 3 at 1.0 hour. Total 4.0 hours, padded 6.0 hours, which equals a 6-hour stop line. The lesson from this example: a feature that sounds small can carry most of the work. Look for the rule hidden in the numbers before you estimate.

Common mistakes

Adding features while you build. "While I am in here, I could also..." is the start of scope creep. Write the idea on the later list and go back to the current feature. If the idea is good, it will still be good next week.

Features you cannot test. "The program is easy to use" cannot be checked by running it. Turn it into something you can see: "the program prints a usage message when the file name is missing". Then test that.

Estimating in your head. A guess kept in your head changes without you noticing. Write each number down, then compare it with the real time when the project ends. That comparison is how your padding factor improves.

Forgetting the reading time. Beginners often estimate the writing and forget the reading. Error messages, documentation and examples take time. Put that time into the padding, and check the reading lessons if you are unsure how long it takes. Reading an error is covered in 02-reading-errors.

Treating the stop line as optional. A stop line you ignore is not a stop line. If you find yourself moving it every time you reach it, the estimate is too small or the scope is too big. Cut a feature instead.

Using a wrong file path, then blaming the code. When the file is not in the folder you run from, the program stops with a clear message:

Traceback (most recent call last):
  File "/home/you/wordcount/wordcount.py", line 6, in <module>
    with open(path, encoding="utf-8") as f:
         ~~~~^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'nosuch.txt'

The first path is the folder of your program. Your folder and Python version will change the path and layout, so the path shown here is only an example. The last line names the file the program could not find. Check the name and the folder before you change the code. A wrong path during testing can waste an hour if you assume the bug is in the logic.

Estimating only the first version. A first version that works is the goal of the project. Improvements that come after it belong to the later list, not to the estimate. This is how a small project stays finished.

Variations and edge cases

A feature you have never built before. Estimate it at twice your normal figure, or split it into a learning task: one session to learn the tool, then the feature. The learning task goes in the plan with its own estimate.

A project that depends on someone else's service or data. The time you wait is not work, but it still uses the stop line. If the service is down, you cut the feature that needs it, or you use a file you control for testing.

A project that finishes early. If the three features work well before the stop line, stop anyway. Write the final note, and move the next idea from the later list to a new plan. Do not add a fourth feature only because time is left over.

A project in a group. Give each person one feature, with the test written down. Agree the stop line for the whole group. The method is the same, but each person needs their own list of checks.

How this appears in interviews and real work

Engineers do this every day under other names. A ticket usually describes one feature and how to check it. The estimate is the number a team gives before work starts, and teams usually add a margin, sometimes called a buffer. A stop line is a time box: a fixed amount of work after which the team reviews what is done and decides what to do next.

In an interview, you may be asked how you would build a small project. A plain answer follows the steps in this lesson. You name the one-sentence goal, the three features you would build first, a rough estimate with a margin, and what you would cut if it ran long. The answer shows that you can finish something, which matters more to a hiring team than a long list of ideas.

Practice

Exercise 1: rewrite a vague list

Rewrite these four vague features as three features that a test could check. Each new feature must name an input and an output.

  • The app looks good and is fast.
  • It has login.
  • It saves data.
  • It works on all devices.

Show the answer

A possible answer, for a note app:

  1. Given a line of text typed into the input box, the program adds it to a list and prints the list in the order it was added.
  2. Given the same list saved to a file, the program loads it again when started, and prints the same list.
  3. Given a bad file name, the program prints a message that names the file and does not crash.

Login and device support are left out. Login is a feature that needs its own plan, and "works on all devices" cannot be checked with a single test. The new features each have an input, an output and a way to check them.

Exercise 2: decide whether it fits

A project has three features with these hour guesses: 1.0, 2.0 and 4.0. The padding factor is 1.5 and the stop line is 8 hours. Does the plan fit? If not, which feature would you cut, and what is the new padded total?

Show the answer

The guess is 7.0 hours. Padded at 1.5, it is 10.5 hours, which is above 8. So the plan does not fit.

Cut the 4-hour feature, because it is the largest single estimate and the plan needs to drop the most time. The two remaining features total 3.0 hours, which pads to 4.5 hours. That fits inside 8 hours with room to spare. A different rule is also reasonable, as long as you write it down. What matters is that the cut follows a rule set before the estimate was checked.

Exercise 3: change the padding and predict

Make a copy of estimate.py and change PADDING from 1.5 to 2.0. Before you run it, predict whether the plan still fits the 6-hour stop line. Then run it.

Show the answer

It does not fit. The guess is still 3.5 hours, and 3.5 times 2.0 is 7.0 hours, which is above 6. The output is:

guess: 3.5 h
padded: 7.00 h
stop line: 6.0 h
does not fit, cut a feature

The change in one number changed the decision. That is why the padding factor should come from your own past work, not from a number you like. A padding factor that is too large makes you cut features you could have built. A factor that is too small makes you cut them late.

Exercise 4: write a stop line and the rule for it

Choose a small project you could finish in a few weeks. Write its stop line as a number of hours, a number of sessions, or a date. Then write the rule you will follow when you reach it, in one sentence.

Show the answer

A sample answer:

Project: a program that counts the number of times each letter appears in a text file.
Stop line: 5 focused hours, or four sessions, whichever comes first.
At the stop line: ship the features that pass, move the rest to the later list, and allow one extension of one hour, written down before it starts.

The stop line has a number, and the rule says what happens when you reach it. Without the rule, the number is only a hope.

Exercise 5: find the hidden feature

Here are three features for a program that converts a list of prices from one currency to another. Which feature has the most hidden work, and why?

  1. Read a list of prices from a file.
  2. Multiply each price by an exchange rate.
  3. Print the converted prices, rounded to two decimal places.

Show the answer

Feature 3 has the most hidden work. Rounding to two decimal places seems simple, but the rounding rule matters: whether to round half up or round to the nearest even value, and whether to round each price separately or the total. A program that rounds each price separately can produce a total that is one cent off from the sum of the rounded prices. The way to handle that is a rule, written down and tested, the same way the bill splitter's leftover cent was handled in Worked example 3. Estimate feature 3 with that in mind, not as a line-print step.

Summary

  • Describe the project in one sentence with an input and an output, so the size is visible.
  • Write each feature so that a test can check it by running the program and comparing the output with a count you made yourself.
  • Cut to three features. Move the rest to a later list, so good ideas are kept and not built now.
  • Estimate each feature in hours, write the numbers down, and multiply the total by a padding factor you can adjust later.
  • Set a stop line in hours, sessions or a date, and decide in advance what you will do when you reach it.
Check yourself: Breaks and project scope (8 questions) →

Stuck? Ask the tutor

Ask about anything in this lesson. Answers are short and use simple words.

Sign in to ask the tutor.

← Taking breaks and avoiding burnout