Most developers we talk to are using GitHub Copilot wrong. They’re treating it like a chatbot—asking it questions, waiting for responses, breaking their flow to have conversations with AI. But Copilot’s real superpower isn’t the chat feature. It’s that subtle gray text that appears as you type: the “ghost text” suggestions that let you code at the speed of thought.
We’ve been experimenting with ghost text workflows for months, and the speed improvements are genuinely surprising. When you learn to work with ghost text instead of against it, you start coding in a completely different way—one where the AI anticipates your next move and you just flow from one completion to the next.
What Makes Ghost Text Different
Ghost text isn’t just autocomplete on steroids. It’s predictive coding that understands context, patterns, and intent. Instead of suggesting the next word, it’s suggesting the next logical block of code based on everything it knows about your project.
The key difference is timing. Chat-based AI tools require you to stop, think, formulate a question, wait for an answer, then integrate that answer back into your code. Ghost text happens in real-time as you’re already typing, so there’s no context switching.
The Ghost Text Mindset Shift
To get the most out of ghost text, you need to change how you approach coding. Instead of thinking “I need to write a function that does X,” start thinking “I need to begin typing what I want, and let Copilot fill in the obvious parts.”
This means:
- Write descriptive variable names and function names first
- Start with comments that describe what you’re building
- Type the scaffolding and let Copilot fill in the implementation
- Accept suggestions even if they’re 80% right, then modify them
The goal is to maintain momentum. You’re not looking for perfect code from Copilot—you’re looking for code that gets you 80% of the way there so you can focus on the interesting 20%.
5 Ghost Text Patterns That Actually Work
1. The Comment-Driven Development Pattern
Start by writing a comment that describes what you want to build. Ghost text will often suggest the entire implementation.
Type this:
// Function to debounce API calls with cleanup
Copilot suggests:
// Function to debounce API calls with cleanup
function useDebounce(callback, delay) {
const [debounceTimer, setDebounceTimer] = useState(null);
const debouncedCallback = useCallback((...args) => {
if (debounceTimer) {
clearTimeout(debounceTimer);
}
const newTimer = setTimeout(() => {
callback(...args);
}, delay);
setDebounceTimer(newTimer);
}, [callback, delay, debounceTimer]);
useEffect(() => {
return () => {
if (debounceTimer) {
clearTimeout(debounceTimer);
}
};
}, [debounceTimer]);
return debouncedCallback;
}
You just described what you wanted and got a complete custom hook. Now you can modify it to fit your exact needs.
2. The Descriptive Variable Pattern
Use descriptive variable names that hint at what the code should do. Ghost text picks up on these hints.
Type this:
const sortedProductsByPriceDesc =
Copilot suggests:
const sortedProductsByPriceDesc = products.sort((a, b) => b.price - a.price);
The variable name told Copilot exactly what transformation you wanted.
3. The Context Building Pattern
Build context gradually. Each line gives Copilot more information about what you’re building.
Type:
const UserProfileCard = ({ user }) => {
const [isEditing, setIsEditing] = useState(false);
if (isEditing) {
Copilot suggests:
if (isEditing) {
return (
<div className="user-profile-card editing">
<input
type="text"
value={user.name}
onChange={(e) => setUser({...user, name: e.target.value})}
/>
<input
type="email"
value={user.email}
onChange={(e) => setUser({...user, email: e.target.value})}
/>
<button onClick={() => setIsEditing(false)}>Save</button>
<button onClick={() => setIsEditing(false)}>Cancel</button>
</div>
);
}
It understood from the context that you’re building an editable user profile component.
4. The Test-Driven Ghost Text Pattern
Write your test assertions first. Ghost text will often suggest the implementation that makes the tests pass.
Type:
// Test
expect(calculateShippingCost(items, 'express')).toBe(15.99);
expect(calculateShippingCost(items, 'standard')).toBe(5.99);
expect(calculateShippingCost([], 'express')).toBe(0);
// Implementation
function calculateShippingCost(items, shippingType) {
Copilot suggests:
function calculateShippingCost(items, shippingType) {
if (items.length === 0) return 0;
const rates = {
express: 15.99,
standard: 5.99
};
return rates[shippingType] || 0;
}
The test cases gave Copilot enough context to suggest a working implementation.
5. The Scaffolding Pattern
Create the structure first, then let ghost text fill in the details.
Type:
class DataProcessor {
constructor(config) {
}
async process(data) {
}
validate(data) {
}
transform(data) {
}
}
Now place your cursor in each method and let Copilot suggest implementations based on the method names and class context.
Training Ghost Text to Know Your Patterns
Ghost text gets better the more it understands your coding patterns. Help it learn by:
Being consistent with naming conventions. If you always name API response handlers handleApiResponse
, Copilot will start suggesting that pattern.
Writing similar code in similar ways. If you have a pattern for error handling, use it consistently. Copilot will pick up on it and suggest it in new contexts.
Including context in file names and folder structure. Copilot can see your file structure and will make suggestions based on where you are in your project.
Adding type hints and JSDoc comments. Even in JavaScript, type information helps Copilot make better suggestions.
When to Accept, Modify, or Reject
Not every ghost text suggestion is worth accepting. Here’s our decision framework:
Accept immediately if:
- The suggestion is exactly what you wanted
- It’s close enough that accepting is faster than typing
- It’s boilerplate code you would have written anyway
Accept and modify if:
- The logic is right but variable names need changing
- The structure is good but implementation details are off
- It’s 80% correct and you can quickly fix the rest
Reject if:
- The suggestion goes in a completely different direction
- It’s more complex than what you need
- It introduces patterns inconsistent with your codebase
The key is speed of evaluation. You should be able to decide accept/modify/reject within a second of seeing the suggestion.
Avoiding Ghost Text Pitfalls
Don’t let it dictate your architecture. Ghost text is great for implementation details but shouldn’t drive high-level design decisions.
Don’t accept suggestions you don’t understand. If you can’t quickly verify that a suggestion makes sense, it’s probably not worth the risk.
Don’t become dependent on it for basic syntax. Ghost text should speed up your coding, not replace your fundamental programming knowledge.
Don’t accept everything. The goal isn’t to maximize acceptance rate—it’s to maintain coding velocity while writing good code.
The Compound Effect
The real magic happens when you start chaining ghost text suggestions together. You accept one suggestion, start typing the next logical piece, get another suggestion, accept that, and keep flowing. You enter a state where you’re essentially having a real-time coding conversation with an AI that understands your intent.
We’ve found that developers who master this workflow often report feeling like they’re coding at 2-3x their normal speed, not because each individual suggestion is groundbreaking, but because the cumulative effect of staying in flow state is enormous.
The best part? You’re still writing the code. You’re still making the decisions. Ghost text just removes the friction of translating your ideas into syntax, letting you focus on the interesting problems instead of the mundane ones.
Try approaching your next coding session with ghost text as your pair programming partner. Start with comments, build context gradually, and learn to work with the suggestions instead of fighting them. You might be surprised by how much faster you can move when the computer anticipates your next thought.