GitHub Copilot: Zero to Hero — Build Your First Ruby Project with AI Pair Programming
Learn Copilot by building a complete Ruby CLI Todo app from scratch with AI pair programming — no fluff, just code.

Audience Software engineers, backend developers, tech leads
Reading Time: ~50 minutes
Prerequisites: Ruby installed (ruby -v
), basic CLI knowledge, VS Code with GitHub Copilot & Copilot Chat extensions
Why now: AI coding assistants like GitHub Copilot are rapidly becoming standard in professional workflows. Teams adopting it effectively see up to 30% faster feature delivery.
TL;DR:
- Build a complete Ruby CLI Todo app with Copilot
- Add automated Minitest coverage and JSON persistence
- Use Copilot for generation, refactoring, and error handling
- Walk away with a tested, documented project in under an hour
⚠️ Disclaimer: All scenarios, accounts, names, and data used in examples are not real. They are realistic scenarios provided only for educational and illustrative purposes.
Problem Definition
The challenge: Developers want to learn GitHub Copilot quickly but most resources are abstract or only cover autocomplete tricks.
Who faces this: Engineers new to Copilot or teams evaluating rollout for real projects.
Cost of inaction: Without structure, developers waste time experimenting, produce fragile code, and miss Copilot’s full potential (tests, refactoring, docs).
Why standard advice fails: Most tutorials show isolated prompts, not how to go from a blank folder to a working, tested, persistent app.
Solution Implementation
Approach: Learn Copilot by building a complete Ruby CLI Todo app, guided step-by-step through generation, refactoring, testing, persistence, and documentation.
Step 1: Setup (~5 minutes)
mkdir todo_ruby && cd todo_ruby
code .
💡 Tip: Copilot works with Ruby out of the box — no config needed.
Step 2: Generate the Core Class (~5 minutes)
Create todo.rb
:
# A simple command-line Todo app in Ruby
class TodoApp
def initialize
@tasks = []
end
def add_task(task)
@tasks << { text: task, done: false }
end
def list_tasks
@tasks.each_with_index do |task, i|
status = task[:done] ? "[x]" : "[ ]"
puts "#{i+1}. #{status} #{task[:text]}"
end
end
def mark_done(index)
@tasks[index][:done] = true if @tasks[index]
end
end
✔️ Skeleton complete.
Step 3: Build a CLI Runner (~10 minutes)
class TodoApp
# ... same as above ...
end
# CLI runner
app = TodoApp.new
loop do
puts "Commands: add <task>, list, done <index>, quit"
input = gets.strip.split(" ", 2)
command, arg = input[0], input[1]
case command
when "add"
app.add_task(arg)
when "list"
app.list_tasks
when "done"
if arg && arg.to_i > 0
app.mark_done(arg.to_i - 1)
else
puts "Invalid index"
end
when "quit"
break
else
puts "Unknown command"
end
end
✔️ Working CLI app with error handling.
Step 4: Add Tests (~10 minutes)
test_todo.rb
:
require "minitest/autorun"
require_relative "todo"
class TodoAppTest < Minitest::Test
def setup
@app = TodoApp.new
end
def test_add_task
@app.add_task("Buy milk")
assert_equal 1, @app.instance_variable_get(:@tasks).length
end
def test_list_tasks
@app.add_task("Buy eggs")
assert_output("1. [ ] Buy eggs
") { @app.list_tasks }
end
def test_mark_done
@app.add_task("Do laundry")
@app.mark_done(0)
assert @app.instance_variable_get(:@tasks)[0][:done]
end
def test_mark_done_with_invalid_index
@app.add_task("Cook dinner")
assert_silent { @app.mark_done(99) }
end
end
✔️ Full happy + sad path test coverage.
Step 5: Add Persistence (~10 minutes)
Update todo.rb
:
require "json"
class TodoApp
def initialize(file = "tasks.json")
@file = file
@tasks = File.exist?(@file) ? JSON.parse(File.read(@file), symbolize_names: true) : []
end
def save
File.write(@file, JSON.pretty_generate(@tasks))
end
def add_task(task)
@tasks << { text: task, done: false }
save
end
def mark_done(index)
@tasks[index][:done] = true if @tasks[index]
save
end
end
✔️ App persists across runs.
Step 6: Refactor with Copilot (~5 minutes)
Prompt Copilot: “Refactor TodoApp to use a Task class instead of hash, keeping API the same.”
Copilot suggests:
Task = Struct.new(:text, :done, keyword_init: true)
✔️ Modernized, idiomatic Ruby.
Step 7: Generate Documentation (~2 minutes)
Copilot can generate a README.md
with commands and usage examples automatically.
Step 8: Run the App (~5 minutes)
ruby todo.rb
Example session:
> add Learn Copilot
> add Write blog post
> list
1. [ ] Learn Copilot
2. [ ] Write blog post
> done 1
> list
1. [x] Learn Copilot
2. [ ] Write blog post
> quit
✔️ End-to-end project complete.
Validation & Monitoring
- Tests: Run
ruby test_todo.rb
→ all green ✅ - Persistence: Exit and restart app, tasks remain
- Failure mode: Invalid input is handled gracefully
- Troubleshooting: If JSON is corrupted, delete
tasks.json
and restart
❗ Warning: Copilot defaults to happy paths — always test invalid cases.
Takeaways
- Copilot accelerates building real apps, not just snippets.
- Tight, specific prompts produce better results than vague ones.
- Always add tests and error handling — Copilot won’t by default.
- Treat Copilot’s code like a junior developer’s PR: review before merging.
Next Steps
Extend your app:
- Add priorities, due dates, deletion, categories
- Build a web UI with Sinatra
Level up with Copilot:
- Use it for PR summaries, code reviews, docs
- Try prompt recipes for debugging and architecture changes
Acronyms
- CLI – Command Line Interface
- JSON – JavaScript Object Notation
- PR – Pull Request
References
- GitHub Copilot Docs - GitHub Copilot Documentation, 2024
- Ruby Style Guide - Ruby Style Guide, 2024
- Minitest Framework - Minitest GitHub, 2024
Comments & Discussion
Share your thoughts, ask questions, or start a discussion about this article.