Skip to content
Agentic Levels

Everything starts here.

GuestLocal progress only
PreferencesSign in
01Start with one taskBest first move for beginners.02Check your LevelMeasure where you are.03Score an AI resultFind the habit to practice first.04Return to Your WorkScores, links, and checkpoints.
Start here

Begin

HomeThe main entry point.New to AIStart with one useful task.
Know where you are

Measure

Check your LevelUse this after you have tried AI.Fluency ScoreScore an AI result you can review.
Build the habit

Learn

LevelsLessonsTracks
Find the reference

Library

PromptsReferenceResourcesCompare Tools
Turn it into work

Apply

Your Next MoveChoose what AI should change next.Tool SetupGet the tools ready.
Come back later

Return

Your WorkScores, links, and checkpoints.My PathContinue from your level.Updates
Site

Site

PricingAboutFAQ & FeedbackPreferences

© 2026 Fuentes Studio

Privacy·Terms
yourCouncil
Ready to help
✦

What do you want to understand?

Ask anything about what you're learning.

← Back
L2Lesson 1Free

Part of the Level 2 core path · Lesson 1 of 7

Write Stubs the Model Can Finish

After this, you'll be able to write function stubs that reliably produce useful inline completions, instead of vague or wrong ones.

Before you start

Before diving in, complete Set Up Your Persistent Workspace so the model already knows your project context before you write your first stub.

The idea

Inline completions are pattern-matching engines. They predict what comes next based on what you've already written. The quality of the prediction is almost entirely a function of how much information you packed into the stub.

A vague stub block leaves the completion bead guessing in open space.
The starting state for Write Stubs the Model Can Finish.
Use this model to move from the starting mistake to the lesson check.
BeforeAfter
HabitGuess from a loose requestUse the lesson move
Work moveSkip Write Stubs the Model Can FinishApply Write Stubs the Model Can Finish
CheckNo clear proofPass the lesson check

The after column is the lesson target.

Here is the before and after: Two stubs for the same function. First: `function processOrders() {`. The model has no idea what Orders look like, what processing means, or what you expect to get back. It will hallucinate a plausible-looking body. Second: `function processOrders(orders: Order[]): { fulfilled: Order[]; failed: Order[] } {`. Now the model knows the input shape, the output shape, and the return structure. The completion it generates will use your actual types, respect the `fulfilled`/`failed` split, and almost certainly be usable.

Three things that make stubs completable. Typed parameters (the model sees exactly what it is working with). A descriptive name that signals intent, not just what (not `process` but `validateAndCharge`). A return type or a comment that describes the expected output. You do not need all three every time. Even one of them lifts the completion noticeably.

Try it (12 min)

Watch out for

  • Accepting the first completion without reading it. The model completes confidently regardless of whether it is correct.
  • Writing function names like `handle`, `process`, or `doStuff`. These give the model almost nothing to work with.
  • Skipping the return type in TypeScript or Python. It is the single highest-value addition to a stub.
  • Expecting the model to know your domain types without seeing them. Open the file that defines your types before you start completing call sites.
  • Treating a bad completion as a tool failure. Nine times out of ten it is a stub problem, not a model problem.

Paste this into Claude

I want to practice skeleton-driven completions. Here is a function stub I wrote that got a bad or vague completion: [paste your stub here, e.g. a plain `function doThing() {` with no types or detail]. Help me rewrite it as a completable stub: (1) Add typed parameters that describe the input shape. (2) Add a return type or an inline comment describing what the function produces. (3) If the name is vague, suggest a more intent-revealing name. Then explain why each change makes the completion more predictable. I will test the new stub in my editor and report whether the completion improved.

Created by potrace 1.16, written by Peter Selinger 2001-2019 What a good response looks like

Here is the rewritten stub for your `processOrders` function:

Before: `function processOrders() {`

After:
```typescript
/**
 * Validates each order against inventory and marks it fulfilled or failed.
 * Does not mutate the input array.
 */
function processOrders(orders: Order[]): { fulfilled: Order[]; failed: Order[] } {
```

Three changes and why each one matters. First, typed parameter `orders: Order[]`: the completion engine now knows the exact shape of what it is iterating over, including every field on your `Order` type. Second, return type `{ fulfilled: Order[]; failed: Order[] }`: the model knows it needs to split into two arrays, not return a boolean or a count. Third, the JSDoc comment: 'validates against inventory' and 'does not mutate' tells the model the business logic without requiring it to guess. When you trigger the completion now, you should see the body use your actual `Order` fields (like `order.id`, `order.quantity`) instead of invented ones. If it still invents fields, open your `Order` type definition file in another tab before completing.

Created by potrace 1.16, written by Peter Selinger 2001-2019 What good looks like

  • You rewrote at least one stub from a blank/untyped version to a typed version with a return type
  • The completion your editor produced for the new stub was noticeably more specific and usable
  • You can name the three elements that make a stub completable: typed parameters, descriptive name, return type or comment
  • You did not have to edit the generated completion more than one or two lines to make it correct
  • You can describe in one sentence why typed stubs produce better completions than blank ones

Created by potrace 1.16, written by Peter Selinger 2001-2019 Go deeper (15 min)

Paste this into Claude

Take a function you already wrote with AI completion. Strip it back to just the stub: signature, types, and a short comment. Delete the body. Ask your editor to complete it again from the stub only. Compare the two versions: the original completion and this one. Paste both here and tell me: which version is closer to what you actually needed, and why? If the second one is worse, what is missing from your stub that the first session had as surrounding context?

Created by potrace 1.16, written by Peter Selinger 2001-2019 What a good response looks like

Here is the comparison you asked for:

Original completion (from full session with surrounding context): The body used `this.inventoryService.check(order.sku)` and correctly referenced `order.customerId` for logging. It matched your actual service dependencies.

Regenerated from clean stub only: The body invented `checkStock(item)` and used `order.itemId` instead of `order.sku`. Both wrong. The model did not have your `InventoryService` import or your `Order` type visible, so it guessed.

What the original session had that the clean stub did not: your `InventoryService` import at the top of the file and 3 other functions in the same file that used `order.sku` as the identifier. The model pattern-matched from those. When you stripped the file to just the stub, that surrounding context disappeared.

Fix for round 3: Add a one-line comment to the stub: `// Uses this.inventoryService.check(sku: string)` and a typed parameter `order: Order`. That gives the regeneration enough signal to get `order.sku` right without needing the full file context.

Created by potrace 1.16, written by Peter Selinger 2001-2019 What good looks like

  • You deliberately regenerated a completion from a clean stub rather than accepting the first one you got
  • You compared the two completions and identified what made one better or worse
  • You adjusted the stub based on what you learned and got a third completion that was at least as good as the original
  • You can explain one specific element of surrounding context that influenced the original completion

When this breaks

  • Breaks when the function has no clear input or output contract, because the model has nothing to pattern-match against and will invent shapes that look plausible but do not exist in your codebase.
  • Breaks in domains the model has weak training coverage on, because typed stubs help but cannot fully compensate for missing knowledge of your specific framework or internal conventions.

AI can help with this

Paste a vague stub into a chat with Claude and say: 'Rewrite this as a completable stub. Add typed parameters, a return type, and rename it if the name is vague. Explain each change.' You will get a template you can paste back into your editor before completing.

Typed inputs, intent, and output-shape blocks frame the stub so the bead completes the right shape.

Created by potrace 1.16, written by Peter Selinger 2001-2019 You can now

✓

Rewrite a vague untyped stub into one with typed parameters, an intent-revealing name, and a return type or comment, and demonstrate that the resulting completion needs at most one or two lines of editing.

Key takeaways

The completion is only as good as the stub. Type your parameters, name your intent, declare your return shape. Then hit Tab.

  1. 1The completion is only as good as the stub you give it
  2. 2Typed parameters, intent-revealing names, and return types are the three signals that lift suggestions
  3. 3Even one of the three lifts the completion noticeably; you do not need all three every time
  4. 4Bad completions are almost always a stub problem, not a model problem

Created by potrace 1.16, written by Peter Selinger 2001-2019 Go deeper

  • Cursor Docs: Getting Started
  • Claude Code Quickstart
  • GitHub Copilot Overview

Was this helpful?

← Back to lessons