A few weeks ago, I gave a talk at a local meetup where I walked through a real feature I was building for Pedro, my self-hosted chatbot. It’s not a greenfield “tutorial” project or hype; just me, my terminal, and the tools I actually use to ship code every day.
Here’s the full recording:
I don’t call this “vibe coding.” What I showed is much more boring and much more reserved, and it’s how I actually leverage AI as a software engineer realistically, safely, and without outsourcing the thinking.
As I have continued to attend meetups and chat with engineers, it surprises me how many people are NOT using AI in their daily workflows, so I am sharing exactly how I do. This is the only way I have been building software for the past few months, and I think every engineer should experiment with how AI can improve their workflows. If you can pull one thing out and implement it, you will have an advantage in the coming years and AI reshapes what is software engineering as we know it.
Choosing a Coding Agent Is Like Choosing an IDE
Right now, my preferred coding agent is Claude Code with the Sonnet 4 1M-token model. I don’t think it’s objectively better than Cursor or Gemini CLI, it just fits the way I already work. I’m terminal-native. If I’m not inside Claude Code, I’m in NeoVim. I avoid UI-heavy workflows, and Claude Code lets me operate in the environment where I’m the most productive which is really the whole reason I use it. Just like IDEs, the best agent is the one that supports your flow instead of interrupting it. Jetbrains and Zed have advanced AI tools, VS Code has Kline, or you can use Cursor or Windsurf. It doesn’t matter, they all use similar or the same models, so you can tune your workflow to your comfort.
NOTE: in the video I demo Crush, a TUI by Charmbracelet. It is super cool and great of you want to leverage APIs or self-hosted AI.
What is a Context Window and Why Context Windows Are Critical
There is a book that outlines the philosophy of only building software that fits in your head [link], and I’ve prescribed to this philosophy for a long time. If I cannot logically reason about the entire control flow of the piece of software, it’s too complex. AI works the same way.
The context window is how much text you can pass to the AI for it to parse and “reason about” (LLMs and transformers predict rather than reason, but their predictions are more accurate based on how details the information you provide as context.). So a context window is the size of the payload.
Self-hosted models I run locally cap out around a few thousand tokens, which is enough for a handful of files and a few logs. The 1M-token context window inside Claude Code is an entirely different experience, because a context window is just how much the model can hold in its working memory. With a large window, I can load the ticket, the relevant code paths, my deployment configuration, a Notion one‑pager, staging logs, and all the debug output in one place. Then I stay in a single, uninterrupted session instead of constantly re‑pasting context.
I still scope everything tightly (aka not vibe coding). One ticket, one feature, and one set of changes. I still hold the system architecture in my head; the model just remembers the text better.
When I say I scope everything to one ticket or one task, that scope also includes the debugging that inevitably comes with it. Because Claude Code can run Bash commands directly in its terminal UI,and because I can explicitly whitelist the tools it’s allowed to use, it keeps the output from those commands in the same session. That continuity is incredibly useful. I don’t have to jump to Stack Overflow for some obscure, one-off deployment error; it already has the logs and can surface the relevant patterns. It’s also great for writing tests, because the model knows exactly what behavior it’s trying to reproduce after seeing the failure modes in the logs. When I deploy something to dev or staging (which lately is a lot of Argo Workflows) I can trigger the endpoint, run the workflow, then let Claude Code use kubectl or whatever CLI I’ve configured to pull logs and verify whether the system is behaving correctly. If it isn’t, it helps me step through the issue. Most of the time I already know the fix: a permissions error means I need to update my IAM scopes, so I edit Terraform manually and then tell Claude Code to rerun the test. But it also adds value on the administrative side, because it can annotate the PR or Linear ticket with the debugging steps taken, which is sometimes a compliance requirement. The important part is that I’m running the exact same steps I would manually; the agent just keeps track of the entire flow and keeps it anchored to the original scope of the work.
Planning Still Belongs to the Engineer
Claude Code’s Plan Mode is excellent, but it is not a replacement for engineering thought work. I describe the problem, the architecture, the constraints, the failure conditions, and the tests that actually matter. The agent turns that into an ordered plan, but the intent and correctness still come from me. It helps with the writing, not the reasoning. If you work with a team, this is where architecture docs become vital. Plan and outline architecture with your team and put it in docs, I find markdown docs in the repo work best. Then tell your AI to reference those docs as it is working. This helps the AI make code that WORKS.
This workflow becomes significantly easier with MCP servers. I only use local MCP servers—nothing remote, nothing with broad access. My Linear ticket AI and my Notion one‑pager become the source of truth. If your team wants to keep architecture in a ticketing service or a wiki service, Claude Code can pull that in and leverage it in the plan and implementation as well. Since this is an extra tool call, sometimes it will need a reminder to look at the external docs, but that is simple to add to your prompt.
SOC 2, Tickets, and Removing Menial Work
We have a SOC 2 requirement at work: every PR must be linked to a Linear ticket. Sometimes I start coding before creating the ticket, usually because a bug surfaced or something looked off. In those cases, Claude Code creates the ticket for me. The text is usually verbose garbage, which is honestly fine. That is exactly what AI should do: remove the menial, compliance‑driven work so I can stay focused on the engineering.
Claude.md: My Pre-Commit Contract
Every repo I use with Claude Code includes a Claude.md file. This document isn’t complicated. It captures the rules and guardrails the agent must follow before anything is committed. It includes the linters that must run, the required tests, the need to link to a Linear ticket, and the documentation work that shouldn’t be skipped. It also outlines boundaries (aka what the agent is not allowed to touch, modify, or assume.).
When I’m coding, I’ll say: “Update the steps in Claude.md to remember to test before we push.” That’s it. This keeps my workflow consistent across features and projects.
AI Writes Just as Many Bugs as We Do
Here’s the line that sums up my entire philosophy for AI‑assisted coding:
Per character written, AI creates as many bugs as software engineers. It just writes more characters.
AI is fast, but it is not careful. So as engineers, we have to design workflows that surface the bugs it generates. I have AI help me debug because if it helped generate the code, it should help untangle the consequences of its speed. But you have to remember AI does not reason. It pattern‑matches. It can check coverage, and connect log lines to code paths, but the failure conditions, the edge cases, the weird sequences still have to come from me.
Sometimes I explicitly tell the model to aim for something like “around 70% test coverage for shared tooling,” not because I care about the metric, but because it forces the agent to touch the less obvious parts of the code.
Debugging With AI: Replacing My Browser, Not My Brain
When I’m debugging Pedro or a production service, I paste Kubernetes logs, staging errors, panic traces, and whatever theoretical edge case just exploded. The model connects those logs back to the implemented code we changed. This has mostly replaced the need for dozens of Chrome tabs or repeated StackOverflow searches. It can parse the code faster than I can, especially if it has been some time since I worked on it, or if another team member has made more recent changes. It also writes all my debugging notes back into the Linear ticket or a readme.md, which dramatically improves future maintainability.
A Workflow You Can Drop Into Any Repository
The core of my workflow is simple: establish boundaries, clarify expectations, and give the agent a well‑defined environment to operate in. Below is the narrative version of the scaffolding I use everywhere.
Your Claude.md
This file exists so the model and I are aligned on what “done” means. It outlines the checks that must run before any commit is made, the expectations for tests and documentation, and the restrictions on tool usage. It also encodes the compliance requirements, like linking a PR to a Linear ticket. By capturing expectations explicitly, the model can operate consistently without drifting or making assumptions about the project.
Your Planning Notes
Every feature should have a ticket and a short one‑pager. The ticket captures the problem and acceptance criteria, and the one‑pager captures the architecture, the relevant context, the tests that matter, and the failure conditions that the model will never infer on its own. Together, these documents become the anchor for Plan Mode and keep both of us honest about what needs to be built.
The Local MCP Setup
A local‑only MCP server keeps the agent grounded. It pulls context from Linear, Notion, or local files, but it never reaches out to services I haven’t blessed. This avoids the security pitfalls of models that can browse or fetch arbitrary resources. It also improves reliability. The model stays inside the world I’ve defined for it.
The Debugging Loop
When something breaks, the agent stays inside the same session that produced the change. This continuity matters because it means the model doesn’t need to rediscover context or re‑infer intent. I feed it real logs, and it maps those logs to code paths. I verify the mapping, and it suggests tests or instrumentation. Then I decide what’s correct. Together, we step through the issue. This loop is extremely efficient because the agent does not have to be re‑taught the problem.
Why This Matters
AI coding is only reliable when the engineer controls the boundaries and the AI controls the text manipulation. This workflow keeps the reasoning with me, the memory with the model, and the risk surface tightly constrained. It’s the only way I’ve found to scale both safety and velocity.
Stay Connected
Want to stay updated on what I’m working on? Here’s where you can find me:





Like an idiot, I forgot the link to the book
https://a.co/d/3fwlh1x