{"slug": "code-first-specs-after-a-practical-guide-to-ai-driven-development", "title": "Code First, Specs After: A Practical Guide to AI-Driven Development", "summary": "A developer introduces a combined approach to AI-driven development, pairing Spec-Driven Development with Code-First Spec-Backfill Development to keep code and specifications aligned. The method uses YAML-based specs to guide AI agents like Claude Code and GitHub Copilot, addressing the common problem of specs becoming outdated as code evolves.", "body_md": "In an era where AI agents like Claude Code and GitHub Copilot have become commonplace, the development landscape has undergone a dramatic transformation. Once you issue an instruction, code flows out effortlessly. However, isn't there a lingering issue: do you find yourself repeatedly explaining to the AI—verbally or via chat—what you want built?\n\nThis is akin to **giving driving directions to a driver (AI) without ever telling them the final destination**. While the code you get might work in the moment, after some time passes, you inevitably wonder: \"Why is the implementation structured this way?\" and \"What was the original specification again?\" As code and specs gradually diverge, many teams face this problem regardless of project size.\n\nIn reality, situations like these are commonplace:\n\nSpecs tend to be \"write once and forget\" documents. As implementation progresses, discrepancies between code and specs accumulate, ultimately leaving teams in a state where \"the code *is* the specification.\"\n\nIn this article, I'll introduce a combined approach: **Spec-Driven Development** (where you write a specification first and use it to guide AI implementation), paired with **Code-First Spec-Backfill Development** (where you implement code changes first, then update the spec to match). Together, these approaches keep both code and specifications up-to-date as a single, continuously aligned source of truth.\n\nNote: \"Code-First Spec-Backfill Development\" is a term I've coined for this article. If a similar methodology already exists under a different name, I'd love to hear about it in the comments!\n\nThis article is aimed at readers like you:\n\nYou may have heard of \"Vibe Coding\"—feeding AI loose requirements and letting it write code based on intuition. While this approach excels at rapid prototyping, it struggles with large-scale applications. This article takes the opposite stance: **running AI development on the foundation of a specification**. However, this approach actually has **two distinct directions**.\n\nThe first is **Spec-Driven Development (SDD)**—a workflow where you prepare detailed specifications upfront (requirements, design, detailed specs, implementation plans) and then use those to drive AI agents.\n\nThe logic is straightforward:\n\nBy preparing detailed specs in advance, you can control AI behavior precisely.\n\nWhile Vibe Coding lets \"AI figure it out,\" Spec-Driven Development **\"guides the AI with a design document.\"** This approach is most powerful when you're building an app from scratch with no existing codebase.\n\nOn the other hand, in real-world development, strictly following \"update the spec first, then implement\" for every small change feels unnecessarily restrictive. For minor modifications, it's often more practical to adjust the code directly, verify it works, and then update the spec afterward—the reverse workflow.\n\nThis is **Code-First Spec-Backfill Development (CFSD)**, the second axis for this article. The idea is to work in the \"code → spec\" order—opposite to Spec-Driven Development—while ultimately ensuring that code and specs remain aligned.\n\nNote: \"Code-First Spec-Backfill Development\" is a term I've coined for this article. If a similar methodology already exists under a different name, I'd love to hear about it in the comments!\n\nThese aren't contradictory; rather, **choose which to use based on your project's situation and phase**:\n\nFor existing applications where specs are missing or out of sync with implementation, start by having AI analyze the current code and generate a `spec.yaml`\n\nas a starting point. After human review and corrections, transition to Spec-Driven Development or Code-First Spec-Backfill Development.\n\nRegardless of approach, the ultimate goal is the same: **keep code and specs perpetually in sync**.\n\nThis article uses **YAML** as the specification format.\n\nRecent research emphasizes providing LLMs with information in **structured formats** like JSON or YAML. Elnashar et al. (2025) compared three prompt styles (JSON, YAML, and Hybrid CSV/Prefix) using GPT-4o, demonstrating that prompt format influences output quality, token cost, and processing time. They found YAML offers **excellent balance between readability and efficiency**.\n\nWe adopt YAML for our spec format because:\n\nFirst, use Claude Code or GitHub Copilot to create a `spec.yaml`\n\ndesign document.\n\nThe critical aspect here is **granularity**. Write this specification in enough detail that an AI can implement the entire app just from reading it. Avoid vague language; aim for a **spec that leaves no room for AI confusion**.\n\nBeyond just APIs and database design, I recommend including **controller/service/repository responsibilities, class names, and function/method names** in the spec. This level of detail prevents AI from inventing its own naming conventions and architecture, and makes traceability much easier to maintain.\n\nThat said, writing such detailed specs from scratch is genuinely challenging. Here's my recommendation: **co-create the spec interactively with AI**. Start by conveying a rough outline of the app, then work through each necessary component one by one, writing YAML as you go. This seems time-consuming upfront, but it pays dividends later—once AI has a clear spec, implementation instructions become simple and rework diminishes significantly.\n\n**The same approach applies to existing applications.** If specs are missing or outdated, ask AI to analyze the current code and generate a `spec.yaml`\n\nstarting point. However, don't consider it done after one generation. Instead, engage in repeated dialogue: \"What's the spec for this feature?\" \"What's this API's responsibility?\" \"Is this class structure sound?\" Iteratively refine the spec through conversation until it reflects actual requirements.\n\nIn other words, whether starting fresh or working with legacy code, **don't aim for a perfect spec on day one. Refine it iteratively through dialogue with AI.** Higher-spec quality directly translates to better implementation quality and maintainability.\n\nWhen deciding on language, frameworks, and databases, consult AI—but don't accept its suggestions uncritically. Investigate and verify: \"Is this architecture truly sound?\" \"Are there alternatives?\" AI is a design partner, not a decision-maker. Responsibility for final design and tech choices rests with you.\n\nAt minimum, document these items:\n\n```\nproject:\n  name: TaskManagerApp\n  version: 1.0.0\n  purpose: \"A simple task management app for individuals and teams\"\ntech_stack:\n  frontend:\n    language: TypeScript\n    framework: Next.js\n    styling: Tailwind CSS\n  backend:\n    language: TypeScript\n    framework: NestJS\n  database:\n    type: PostgreSQL\n    orm: Prisma\narchitecture:\n  pattern: MVC\n  directories:\n    controllers: src/controllers\n    services: src/services\n    repositories: src/repositories\n    models: src/models\n    tests: tests\ndatabase_schema:\n  tables:\n    - name: users\n      columns:\n        - { name: id, type: uuid, primary_key: true }\n        - { name: email, type: string, unique: true }\n        - { name: password_hash, type: string }\n    - name: tasks\n      columns:\n        - { name: id, type: uuid, primary_key: true }\n        - { name: user_id, type: uuid, foreign_key: users.id }\n        - { name: title, type: string }\n        - { name: status, type: enum, values: [todo, in_progress, done] }\ncontrollers:\n  AuthController:\n    methods:\n      - login\n      - register\n  TaskController:\n    methods:\n      - list\n      - create\n      - update\n      - delete\nservices:\n  AuthService:\n    methods:\n      - authenticate\n      - createUser\n  TaskService:\n    methods:\n      - getTasks\n      - createTask\n      - updateTask\n      - deleteTask\nrepositories:\n  UserRepository:\n    methods:\n      - findByEmail\n      - create\n  TaskRepository:\n    methods:\n      - findAllByUserId\n      - create\n      - update\n      - delete\napi:\n  - method: POST\n    path: /auth/login\n    controller: AuthController\n    action: login\n    service: AuthService.authenticate\n  - method: GET\n    path: /tasks\n    controller: TaskController\n    action: list\n    service: TaskService.getTasks\n  - method: POST\n    path: /tasks\n    controller: TaskController\n    action: create\n    service: TaskService.createTask\ndto:\n  LoginRequest:\n    email: string\n    password: string\n  LoginResponse:\n    accessToken: string\n  CreateTaskRequest:\n    title: string\n  TaskResponse:\n    id: uuid\n    title: string\n    status: string\nfeatures:\n  - User registration\n  - JWT login\n  - Task CRUD\n  - Task status management\ncoding_rules:\n  naming:\n    controller: PascalCase\n    service: PascalCase\n    repository: PascalCase\n    method: camelCase\n  comments:\n    language: English\ntesting:\n  framework: Jest\n  unit:\n    - AuthService\n    - TaskService\n  integration:\n    - AuthController\n    - TaskController\nconstraints:\n  - Passwords must be hashed with bcrypt\n  - APIs must be implemented as REST\n  - Controllers must not contain business logic\n  - Services must be called before Repositories\nai_rules:\n  - spec.yaml is the single source of truth\n  - Don't change naming conventions\n  - Generate test code alongside implementation\n  - Report differences between spec.yaml and code after implementation\nchangelog:\n  - version: 1.0.1\n    date: \"2026-07-20\"\n    author: yamada\n    summary: \"Added priority field\"\n  - version: 1.0.0\n    date: \"2026-07-15\"\n    author: suzuki\n    summary: \"Initial version\"\nbacklog:\n  - id: TASK-101\n    title: Task search feature\n    priority: High\n  - id: TASK-102\n    title: Email notifications\n    priority: Low\n```\n\nThis YAML is just an example. If you have ideas for clearer spec structures or additional useful sections, please share in the comments!\n\nThe key is meticulously documenting \"what,\" \"in what language/tech,\" and \"in what structure\" you're building. With a spec like this, you never need to repeat explanations to AI—which also **saves tokens**.\n\nOnce you have your spec, prepare the actual development environment.\n\nFirst, adopt Git to track and manage code changes. GitHub Desktop or similar GUIs make this convenient.\n\nFor detailed setup instructions, countless excellent guides exist online, so I'll skip those details here.\n\nNext, install an editor like Visual Studio Code and the Claude Code or GitHub Copilot extensions/CLIs. Refer to official documentation for installation:\n\nOnce your environment is ready, it's time for AI to implement your app. Here's where Spec-Driven Development truly shines. Note: This step assumes you're building a new app. If you already have an application, **skip to Step 4: \"Checking for Discrepancies Between Spec and Implementation.\"**\n\n```\n@spec.yaml\nRead the entire contents and implement the app without omission.\n@spec.yaml\nRead the entire contents and write test code as well.\n```\n\nOnce you submit this prompt, wait for results. Depending on scope, it may take considerable time—be patient.\n\nAfter implementation completes, have AI verify that the spec and actual code align perfectly. This is a **critical step for ensuring traceability**.\n\n```\n@spec.yaml\nReport any discrepancies between the current code and the spec.\n```\n\nWhen discrepancies are reported, assess whether the implementation or spec is problematic, then decide which to correct.\n\n**If the implementation is the problem (code doesn't follow spec.yaml):**\n\n```\nFix the discrepancies you just identified.\nAlign the code with @spec.yaml.\n```\n\n**If the spec is the problem (spec.yaml differs from implementation):**\n\n```\nReflect the changes you mentioned in the following document.\nUpdate @spec.yaml accordingly.\n```\n\nBy constantly synchronizing code and specs this way, you're prepared for the next phase.\n\nThe finished app almost always differs slightly from your original vision. This is where you enter a loop:\n\nRecord the current state with a commit.\n\n```\nModify the code to match (desired changes).\n```\n\nIf the fix looks good, request tests for that change:\n\n```\nRead this change and write test code for it.\n```\n\nIf the implementation is correct (code is right, spec needs updating), update the spec side:\n\n```\nRead this change and update @spec.yaml accordingly.\n```\n\nIf you're satisfied, commit this update too.\n\nContinue cycling through:\n\nBy repeating this cycle, **code and specs stay perpetually synchronized and up-to-date**.\n\nWhile the above describes the initial app launch, **all subsequent feature additions and revisions follow the exact same cycle**. This is where **Code-First Spec-Backfill Development truly shines**.\n\nWhen adding features or making improvements, **start with code**:\n\n```\nModify the code to match (desired changes).\n```\n\nIf the fix looks good, request tests:\n\n```\nRead this change and write test code for it.\n```\n\nIf the implementation is correct, update the spec:\n\n```\nRead this change and update @spec.yaml accordingly.\n```\n\nCommit and you're done.\n\nThis way, **you try things in code first, then reflect them in specs if satisfied**—practicing **Code-First Spec-Backfill Development** repeatedly. For those who found spec-writing tedious before, this framework naturally keeps specs updated with every feature addition or fix. There's no separate \"manually update spec\" task anymore. Every change flows into both code and spec simultaneously, keeping them always aligned.\n\nFor multi-person teams, document **which prompts to use** in your `README.md`\n\n:\n\n```\n## AI Instruction Templates\n\n### For Feature Addition/Fixes\nModify the code to match (desired changes).\n\n### For Test Code Creation\nRead this change and write test code for it.\n\n### For Spec Updates (when implementation exceeds spec.yaml)\nRead this change and update @spec.yaml accordingly.\n\n### For Discrepancy Checks\n@spec.yaml\nReport any discrepancies between the current code and the spec.\n```\n\nThis ensures every team member follows the same procedure at the same quality level. Whoever's working on what, you'll always maintain **current specs** and **traceable code**. This methodology's greatest benefit is consistency across all phases—new development, features, and fixes. Overall project quality improves dramatically.\n\nHere's a summary:\n\nMany engineers privately sense something: we understand the importance of specs and keeping them current. Yet, **in practice, maintaining specs is tedious and joyless**. The reason is simple: **code's behavior is the truth; specs merely retroactively describe it**. Intuitively, we feel **specs should conform to code, not vice versa. Traditional development methods never resolved this paradox.**\n\nAdditionally, team development surfaced another issue: \"Without X, we can't understand the whole picture\"—a concerning dependency. Specs are incomplete or nobody truly understands them. These problems plague organizations regardless of size.\n\nOur development landscape shifted dramatically. AI now generates precise, voluminous code quickly. Human roles necessarily evolved. Projects once requiring large engineer teams can now proceed with smaller groups plus AI. **Blindly following existing methodologies won't keep pace with this rapid change.**\n\nThat's not to say traditional methods are wrong—decades of practice accumulated vast knowledge and case studies. But the \"optimal method now\" and \"optimal method for the future\" may differ.\n\nInterestingly, **agile development initially faced similar criticism** (\"excessive,\" \"lacks discipline\"). Today it's mainstream, spawning variants like Scrum and Kanban. I expect similar evolution for Code-First Spec-Backfill Development.\n\nSome readers will embrace \"Code-First Spec-Backfill Development\" enthusiastically; others will skeptically object. That's natural—**this methodology has weak points and room for improvement**. No perfect development method exists.\n\nIf this article helps engineers facing the challenges described earlier, I'd be delighted.", "url": "https://wpnews.pro/news/code-first-specs-after-a-practical-guide-to-ai-driven-development", "canonical_source": "https://dev.to/nishifeoda/code-first-specs-after-a-practical-guide-to-ai-driven-development-6c", "published_at": "2026-08-01 14:07:36+00:00", "updated_at": "2026-08-01 14:11:31.213608+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "artificial-intelligence", "large-language-models"], "entities": ["Claude Code", "GitHub Copilot", "Elnashar"], "alternates": {"html": "https://wpnews.pro/news/code-first-specs-after-a-practical-guide-to-ai-driven-development", "markdown": "https://wpnews.pro/news/code-first-specs-after-a-practical-guide-to-ai-driven-development.md", "text": "https://wpnews.pro/news/code-first-specs-after-a-practical-guide-to-ai-driven-development.txt", "jsonld": "https://wpnews.pro/news/code-first-specs-after-a-practical-guide-to-ai-driven-development.jsonld"}}