Edit this page


A digitized Copilot-inspired guide in side profile studies a conceptual CaseFlow Rails site; Ruby source, an unverified test and an amber question flow around the browser.

Cover illustration: the CaseFlow browser is a conceptual application view. The companion lab supplies a Rails API source overlay, not a working web interface.

GitHub Copilot for Rails Engineers · Part 01

The first half-hour in a new repository should produce a defensible map, a list of unknowns, and one useful next action.

A client asks: “Can we add status transitions to this Rails API this week?” You have never seen the codebase. GitHub Copilot can help you explore it, but an answer that sounds like Rails is not evidence that this particular application behaves that way. Our goal is a small, falsifiable map of the current request path before proposing a change.

We will use CaseFlow, a synthetic Rails API source overlay. It is deliberately small enough to inspect and incomplete enough to expose an important habit: distinguish what source says, what a test asserts, and what a running system actually did. The companion materials contain the files, bounded prompt, answer guide, diagram source and evidence template.

Contents

0–5 minutes: predict before you inspect {:#predict}

Write down your current hypothesis for POST /api/requests: what input is accepted, what gets saved and what comes back. Label every sentence prediction. This prevents your first impression or an AI suggestion from quietly turning into a fact.

The exercise is about one endpoint. Do not ask Copilot to explain the entire application yet. GitHub’s codebase exploration guide shows how repository and file context can narrow a question. Its review guidance is equally relevant: inspect the answer against actual files. Here, the hypothesis is an investigation aid, not a delivery promise.

5–15 minutes: follow the request through source {:#trace}

Start with caseflow-overlay/config/routes.rb:

namespace :api do
  resources :requests, only: %i[index show create]
end

This declares a create route under /api. Rails routes dispatch incoming HTTP methods and paths to controller actions, as described in the Rails routing guide. The only: list matters: it does not declare an update route. Then inspect app/controllers/api/requests_controller.rb:

def create
  request = ServiceRequest.new(request_params)
  if request.save
    render json: serialize(request), status: :created
  else
    render json: { errors: request.errors.full_messages }, status: :unprocessable_entity
  end
end

def request_params
  params.require(:request).permit(:title, :description)
end

The controller allows title and description from the request payload; status is not permitted there. Its private serialize method selects id, title, description, status, created_at and updated_at. There is no separate serializer file in this overlay. A reader who guesses “the serializer” from habit would be looking in the wrong place.

Next, the model constrains title presence, title length and status membership:

STATUSES = %w[open in_progress resolved].freeze

validates :title, presence: true, length: { maximum: 160 }
validates :status, inclusion: { in: STATUSES }

The migration declares a service_requests table with status defaulting to open. A migration describes a schema change applied during setup; it is not another method called for each POST. In a correctly migrated database, the default is a reasonable expectation for a new record created without an explicit status, but we still have to run the application before claiming to have observed it. See the Rails migration guide for the schema role of migrations.

The dotted link marks schema context, not an extra runtime step. The status labels describe the supplied controller branches; they are not a report of executed responses.

15–20 minutes: ask Copilot for a bounded map {:#ask}

Now use Copilot in VS Code with the relevant files in context. Ask for a map with citations rather than an implementation:

Trace POST /api/requests through the route, controller, model and migration.
Cite the exact file path for each source-backed statement.
Separate facts visible in source from behavior that still needs a run or test.
State what the supplied tests assert without claiming they passed.
List unknown behavior and do not edit files.

Check each path yourself. A citation is useful only if it points to a file that exists and supports that exact claim. Record a missing citation or a mistaken inference; correcting Copilot’s map is part of the exercise. In a client repository, keep sensitive data out of the prompt and follow the organization’s approved tooling rules.

20–27 minutes: turn assertions into observations {:#observe}

The included request spec has two cases: a valid title expecting a 201 response, an open status and one stored record; and a missing title expecting 422 with no record. Those are assertions in source, not reported pass results. The starter contains only an overlay, with no generated Rails application, lockfile or installed dependencies; we have not executed its specs in the authoring environment.

A conceptual Ruby request spec in an editor is surrounded by source and assertion flows; an amber unverified gate precedes an empty observation pane.
Source and a test assertion can suggest behavior. Only an actual run can supply observation; CaseFlow has not crossed that boundary yet.

On a Mac with compatible Ruby, Rails and PostgreSQL installed, follow caseflow-overlay/README.md in the companion materials. Copy the overlay into a newly generated Rails API shell, inspect the diff, add a compatible rspec-rails, and record the actual output of:

bin/rails db:create db:migrate
bundle exec rspec spec/requests/api/requests_spec.rb

If setup fails, keep the first useful error. A blocker is evidence. Do not replace it with a hypothetical green test run. If it succeeds, send a synthetic HTTP request and capture the actual response before promoting the expected behavior to observed behavior.

Statement Evidence now Classification
Route includes create but no update config/routes.rb Source fact
Controller permits title, description RequestsController#request_params Source fact
Migration declares default open Migration source Schema fact; runtime effect pending
Valid request returns 201 in this environment Spec assertion only Unverified until execution
Client can transition to resolved No route/action or test supplied Unsupported

27–30 minutes: give the client one bounded answer {:#client}

The useful answer is not “Copilot says the endpoint works.” It is: “The source defines creation with title and description, title and status validations, and a schema default of open. The supplied specs cover valid creation and missing title, but I have not run them in this environment. I do not see an update route. The next safe step is to boot the overlay, record the focused spec result, then scope a status-transition contract and tests.”

That answer supports a short engagement: a visible finding, a named uncertainty and a testable next issue. It also gives a stakeholder a clear boundary between discovery and implementation.

Your 20–30 minute lab {:#lab}

  1. Make three predictions in a local ENVIRONMENT.md using the template (caseflow-overlay/ENVIRONMENT.template.md).
  2. Trace the five files in the lab checklist (docs/labs/01-codebase-diagnosis.md), then compare Copilot’s cited map against source.
  3. Attempt the focused spec; record the versions, command, actual outcome or first blocker.
  4. Explain why a model that lists resolved does not prove a client can perform a status transition. Draft one bounded issue for that transition.

Retrieval question: What evidence would let you change “the source suggests 201” to “I observed 201,” and why does a passing spec not prove the entire API is production ready?

Next in the series: we will refine the context we give Copilot—repository guidance, instructions and tool boundaries—while keeping the same evidence discipline.

References and supporting materials {:#references}