DEV Community

Alain Airom (Ayrom)
Alain Airom (Ayrom)

Posted on

Spec-Driven Development with IBM Bob SDLC

Using the SDD mode with Bob to implement specifications at the center of AI-assisted software development!

Short Introduction-What is SDD?

In traditional software engineering, the path from an idea to production code is often a communication path. A feature request passes through product briefs, chat threads, and sometimes vague developer assumptions, frequently resulting in a chaotic feedback loop of unexpected bugs, missing edge cases, and code rewrites.

Spec-Driven Development (SDD) completely flips this paradigm. It is an engineering methodology where the machine-readable specification is the absolute source of truth throughout the entire Software Development Lifecycle (SDLC).

The Core Concept: Spec First, Code Second

Under an SDD framework — such as the one established by GitHub’s Spec Kit — development does not begin in an IDE writing application code. Instead, it begins by defining a highly structured, strict, markdown-based contract inside the repository.

This contract is broken down into a deterministic pipeline:

┌─────────────────┐       ┌─────────────────┐       ┌─────────────────┐       ┌─────────────────┐
│  Constitution   │ ───>  │  Specification  │ ───>  │  Technical Plan │ ───>  │  Atomic Tasks   │
└─────────────────┘       └─────────────────┘       └─────────────────┘       └─────────────────┘
   Global rules &            What the feature          How it will be            Granular, testable
   non-negotiables           does (boundaries)         built (the stack)         implementation units
Enter fullscreen mode Exit fullscreen mode

Only when this pipeline is locked and validated do developers (or AI software agents like Bob) begin implementation.

Why SDD is Crucial for Modern & AI-Assisted Engineering

As AI pairs and autonomous agents become deeply integrated into the coding workflow, traditional prompt engineering falls short. Simply telling an AI tool to “build a feature” leads to hallucinated frameworks, ignored edge cases, and fragmented architectures.

SDD serves as the ultimate guardrail for both human developers and AI builders by introducing three key pillars:

  • Elimination of “Habitual” Coding: Developers and AI engines are forced to build strictly according to the explicit instructions in the document, rather than relying on standard programming assumptions or cutting corners.
  • Deterministic AI Execution: By passing an autonomous agent an atomic, isolated task backed by a rigid structural plan and constitution, the context window is optimized. The AI transitions from a creative guesser to a precision compiler.
  • Traceable Single Source of Truth: If an application requirement changes, you don’t rewrite the source code or patch the bugs manually. You update the specification file, regenerate the task list, and rerun the execution pipeline.

> The SDD Bottom Line: > Code is cheap; architecture is expensive. Spec-Driven Development ensures that your design intent is perfectly preserved from the first line of documentation to the final containerized deployment.


Building Spec-Driven Applications with Bob

Just this morning, I noticed that a new Spec-Driven Development (SDD) mode had been released for Bob. My immediate instinct was to dive into the official GitHub Spec Kit documentation to figure out how to integrate this workflow efficiently. However, after analyzing the core principles of the framework, I completely changed my approach. Instead of manually mapping out the pipeline, I pivotally engaged Bob to orchestrate the entire lifecycle directly — driving the process from foundational architecture and markdown documentation all the way to the final execution of the containerized application.


The SDD Blueprint (by Bob): An 8-Phase Execution Roadmap

Spec-Driven Development with Spec-Kit moves away from “one-shot” code generation. Instead, it relies on a strict, 8-phase structural pipeline split into required architectural milestones and optional quality gates.

🧱 Phase 0–1: Establishing the Foundation

  • Prerequisites & Infrastructure Setup: Before writing a single line of code, the global environment is locked down. Spec-Kit’s CLI (specify-cli) is isolated globally via uv tool install, and the repository is initialized natively to interface with Bob using specify init . --ai bob.
  • The Project Constitution (/speckit.constitution): [REQUIRED] The absolute governing law of the repository. This file dictates non-negotiable coding standards, testing thresholds, local privacy boundaries, and container deployment rules. It acts as a static constraint model that Bob cannot violate during development.

📝 Phase 2–3: Defining the Boundaries (The “What”)

  • Functional Specification (/speckit.specify): [REQUIRED] A pure focus on what to build and why, completely stripped of technical or framework assumptions. It outlines user stories, business rules, and explicit out-of-scope boundaries.
  • Ambiguity Clarification (/speckit.clarify): [OPTIONAL] A vital quality gate where Bob analyzes the specification for hidden assumptions, prompts the developer with targeted edge-case questions, and appends the explicit answers directly into the specification file before planning begins.

🏗️ Phase 4–5: Designing the Architecture (The “How”)

  • Technical Implementation Plan (/speckit.plan): [REQUIRED] The transition from product requirements to engineering reality. This phase explicitly maps out the tech stack, library choices, localized database schemas, API contracts, and testing strategies while respecting the boundaries set by the Constitution.
  • Quality Checklist (/speckit.checklist): [OPTIONAL] Validates the completeness of the plan, acting as a structural unit test to confirm that all user stories map to explicit acceptance criteria.

📋 Phase 6–7: Compiling the Action Items

  • Atomic Task Breakdown (/speckit.tasks): [REQUIRED] Bob converts the comprehensive technical plan into an ordered, highly detailed, markdown-based task board (tasks.md). Tasks are rigorously sequenced (Data models → Repositories → Core Logic → UI Layers) and flagged for parallel processing [P] where applicable.
  • Cross-Artifact Analysis (/speckit.analyze): [OPTIONAL] A final automated check ensuring complete parity. It blocks development if a task fails to line up with a specification requirement or violates a rule in the Constitution.

🚀 Phase 8: Controlled Implementation Loop

  • Systematic Execution (/speckit.implement): [REQUIRED] Bob transitions completely into an execution engine. Rather than blindly building an entire application, Bob consumes tasks sequentially from the task board.

The 5-Task Mandatory Guardrail: > To guarantee industrial control, the workflow enforces a strict pause after every 5 tasks. Development stops, tests are executed inside the local container ecosystem, and explicit user confirmation is verified before Bob can mark the checkpoint complete and move to the next development block.

** ⚠️ A Critical Warning on Slash Commands**

✅ Type directly into Bob's Chat Interface
❌ NEVER execute /speckit.* commands in your terminal
Enter fullscreen mode Exit fullscreen mode

A common misconception when first adopting Spec-Kit is treating slash commands as command-line tools. All /speckit.* commands are explicitly designed for your AI agent's chat window, not the terminal. When issued within Bob's workspace chat, they trigger his internal context parsers to read, validate, and alter the structured markdown repository files deterministically.


Bob literally acted as an architectural compass, guiding me step-by-step down the Spec-Kit pipeline. He meticulously surfaced the exact slash commands required to advance from high-level discovery into deep, technical documentation. To kick off the project, Bob even brainstormed a handful of viable application concepts. I ultimate landed on a Personal Finance Tracker — a perfect candidate to validate this industrial workflow, which Bob immediately began scaffolding into a fully functional initial prototype.


Functional Specification: Personal Finance Tracker

The core objective of this specification is to deliver a full-featured, local-first, privacy-focused desktop workspace for comprehensive financial management. The specification explicitly avoids treating requirements as vague feature requests, instead structuring every capability as an independently testable user scenario bound by quantitative performance thresholds.

Core Core User Stories & Testing Scenarios

The specification segments the application requirements into prioritized user stories. Each story features an explicit prioritization rationale and deterministic Given-When-Then acceptance test scenarios.

🔴 Priority 1 (P1): Foundational Core Features

  • User Story 1 — Quick Transaction Entry: Focuses on recording income, expenses, and asset transfers.
  • Acceptance Criteria: Inputting an amount, category, date, and description must persist immediately, display seamlessly in the ledger view, and trigger an instantaneous, accurate update to the global account balance.

  • User Story 2 — Multiple Account Management: Support for isolated tracking across diverse account types, including Cash, Checking, Savings, Credit Cards, and Investment portfolios.

  • Acceptance Criteria: The system must prevent negative balances on non-credit accounts if configured, accurately track inter-account transfers without duplicating totals, and reconcile cleared balances.

  • User Story 3 — Dynamic Category Customization: A hierarchically organized system containing default and user-defined expense and income categories.

  • Acceptance Criteria: Parent-child category mapping must automatically cascade totals up when viewing summaries (e.g., Utilities → Electricity).

🟡 Priority 2 (P2): Management & Operational Features

  • User Story 4 — Comprehensive Budget Creation: Allows users to establish monthly, category-specific spending limits.
  • Acceptance Criteria: The application must automatically monitor real-time transaction matching against budgets and trigger visual warnings at 80% and 100% threshold crossings.
  • User Story 5 — Recurring Transaction Automation: Scheduling recurring income or bills (monthly rent, bi-weekly salary).
  • Acceptance Criteria: Auto-generation of upcoming transaction entities must execute deterministically on specified calendar dates without needing a manual click.
  • User Story 6 — Active Financial Goals Tracker: Setting specific savings targets (e.g., Emergency Fund).
  • Acceptance Criteria: Users must be able to link account balances or allocate manual contributions directly to a goal, displaying an automated progress percentage.

🔵 Priority 3 (P3): Intelligence & Reporting Features

  • User Story 7 — Investment Portfolio Monitor: Manual tracking of asset positions, stock quantities, buy/sell costs, and basic performance gains.
  • Acceptance Criteria: Cost-basis must calculate automatically alongside simple unrealized gains when the current price is adjusted.
  • User Story 8 — Cash Flow Forecasting: Rolling 6-month historical projections calculating future balances by intersecting current asset levels against budgets and recurring items.
  • Acceptance Criteria: Dynamic updates must redraw instantly if a recurring bill is modified or added.
  • User Story 9 — Advanced Reporting & Analytics: Visual multi-axis chart distributions outlining spending breakdowns, income trends, and net worth progress.
  • Acceptance Criteria: Must support instant cross-filtering by date ranges, specific subsets of accounts, or categories.
  • User Story 10 — Standardized Data Import & Export: Bulk utilities handling data ownership via files.
  • Acceptance Criteria: Ingestion of external banking templates via structured formats (CSV/OFX) and absolute export to local, unencrypted CSV/PDF formats for external auditing or data freedom.

Non-Functional Constraints & Success Criteria

The specification outlines strict performance, scale, and environment guardrails that Bob must comply with during the code-generation cycle:

| Identifier | Constraint Domain        | Success Threshold / Criteria                                 |
| ---------- | ------------------------ | ------------------------------------------------------------ |
| **SC-001** | **Data Isolation**       | Strict local-only database sandbox. Zero cloud telemetry or external network calls. |
| **SC-002** | **Performance UI**       | Sub-100ms processing delay for filtering, searching, and drawing ledgers. |
| **SC-003** | **Volume Handling**      | Smooth layout rendering supporting 10,000+ active transactions without lag. |
| **SC-004** | **Database Scale**       | Seamless IndexedDB file integrity tested up to a limit of 50,000 entities. |
| **SC-005** | **Calculation Accuracy** | Floating-point error elimination. All math operates via safe rounding logic. |
| **SC-010** | **Bulk IO Performance**  | Complete parsing/exporting of 50,000 rows in under 30 seconds. |
| **SC-011** | **Fault Tolerance**      | Zero-data-loss execution via transactional database state controls. |

With the Spec-Driven Development (SDD) contract established, Bob translates the functional requirements into a strict, production-grade codebase. To enforce long-term maintainability, strict local privacy, and deterministic reliability, the Personal Finance Tracker is engineered using Clean Architecture principles and a decoupled, single-responsibility layer layout.
Enter fullscreen mode Exit fullscreen mode

3. Scope Boundaries & Technical Assumptions

To maintain project control and ensure delivery within predictable cycles, the specification isolates clear out-of-scope boundaries to guide development:

  • No Automated Bank Synchronizations (v1): To enforce absolute data privacy and reduce network complexity, all transaction entries are manual or driven exclusively by user-triggered local file imports (CSV/OFX).
  • Single Currency Engine: Multi-currency portfolios and complex FX conversion tables are deferred. The system calculates entirely under a single baseline currency symbol.
  • No Real-Time Market Data Streams: Stock or fund price adjustments within the investment tracker are managed through explicit, user-provided manual inputs rather than third-party API tracking.
  • Single-User Workspace: Multi-user logins, cloud authentication layers, and real-time family account syncing are entirely out of scope. Security is maintained locally by the user’s host operating system.

Architecture Deep Dive: The Personal Finance Tracker-Technical Implementation

With the Spec-Driven Development (SDD) contract established, Bob translates the functional requirements into a strict, production-grade codebase. To enforce long-term maintainability, strict local privacy, and deterministic reliability, the Personal Finance Tracker is engineered using Clean Architecture principles and a decoupled, single-responsibility layer layout.


The Core Architectural Pillars

The system architecture is anchored to several non-negotiable structural constraints:

  • Privacy-First Isolation: To guarantee data ownership, there is zero cloud synchronization or external API communication. All financial data is persisted locally in the client environment via IndexedDB.
  • Deterministic Type Safety: Built using TypeScript strict mode with a mandatory prohibition of the any keyword.
  • Industrial Code Quality: Files are subject to a strict maintainability limit of 200 lines maximum per file.
  • The Result Pattern: To prevent unexpected application state or silent crashes, exception throwing is replaced with explicit type handling via a Result<T, E> return wrapper.

Decoupled Layer Breakdowns

The application logic flows strictly from the outside inward, protecting core business workflows from infrastructure side effects.

🌌 I. Domain Layer (Core Business Domain)

The absolute center of the system, completely decoupled from frontend frameworks, database libraries, and external tools.

  • Entities: Primitives containing core mutable operational business logic (e.g., Transaction, Account, Budget).
import { Result, ok, err } from '@/shared/types/result';
import { SaveError, ValidationError, NotFoundError } from '@/shared/types/errors';
import { Transaction } from '@/domain/entities/transaction';
import { TransactionRepository } from '@/application/ports/transaction-repository';
import { AccountRepository } from '@/application/ports/account-repository';
import { CategoryRepository } from '@/application/ports/category-repository';

export interface AddTransactionInput {
  type: 'income' | 'expense' | 'transfer';
  amount: number;
  date: string;
  accountId: string;
  categoryId?: string | null;
  description: string;
  payee?: string | null;
  tags?: string[];
  transferToAccountId?: string | null;
}

export class AddTransactionUseCase {
  constructor(
    private readonly transactionRepository: TransactionRepository,
    private readonly accountRepository: AccountRepository,
    private readonly categoryRepository: CategoryRepository
  ) {}

  async execute(input: AddTransactionInput): Promise<Result<Transaction, SaveError | ValidationError | NotFoundError>> {
    // Validate account exists
    const accountResult = await this.accountRepository.findById(input.accountId);
    if (!accountResult.ok) {
      return err(new NotFoundError('Account not found', { accountId: input.accountId }));
    }

    // Validate category if provided
    if (input.categoryId) {
      const categoryResult = await this.categoryRepository.findById(input.categoryId);
      if (!categoryResult.ok) {
        return err(new NotFoundError('Category not found', { categoryId: input.categoryId }));
      }
    }

    // Validate transfer account if transfer type
    if (input.type === 'transfer') {
      if (!input.transferToAccountId) {
        return err(new ValidationError('Transfer requires transferToAccountId'));
      }
      const transferAccountResult = await this.accountRepository.findById(input.transferToAccountId);
      if (!transferAccountResult.ok) {
        return err(new NotFoundError('Transfer account not found', { accountId: input.transferToAccountId }));
      }
    }

    // Create transaction
    try {
      const transaction = Transaction.create({
        type: input.type,
        amount: input.amount,
        date: input.date,
        accountId: input.accountId,
        categoryId: input.categoryId,
        description: input.description,
        payee: input.payee,
        tags: input.tags,
        transferToAccountId: input.transferToAccountId,
      });

      // Save transaction
      const saveResult = await this.transactionRepository.save(transaction);
      if (!saveResult.ok) {
        return err(saveResult.error);
      }

      // Update account balance
      const account = accountResult.value;
      let newBalance = account.balance;

      if (input.type === 'income') {
        newBalance += input.amount;
      } else if (input.type === 'expense') {
        newBalance -= input.amount;
      } else if (input.type === 'transfer') {
        newBalance -= input.amount;
      }

      const updatedAccount = account.updateBalance(newBalance);
      await this.accountRepository.update(updatedAccount);

      // Update transfer destination account if transfer
      if (input.type === 'transfer' && input.transferToAccountId) {
        const transferAccountResult = await this.accountRepository.findById(input.transferToAccountId);
        if (transferAccountResult.ok) {
          const transferAccount = transferAccountResult.value;
          const updatedTransferAccount = transferAccount.updateBalance(
            transferAccount.balance + input.amount
          );
          await this.accountRepository.update(updatedTransferAccount);
        }
      }

      return ok(saveResult.value);
    } catch (error) {
      if (error instanceof ValidationError) {
        return err(error);
      }
      return err(new SaveError('Failed to add transaction', { error }));
    }
  }
}

// Made with Bob
Enter fullscreen mode Exit fullscreen mode
  • Value Objects: Immutable domain blocks enforcing self-validation rules upon instantiation (e.g., Money, DateRange).

⚙️ II. Application Layer (Use Cases)

Defines the concrete functional operations of the platform.

  • Use Cases: Isolated, single-purpose command engines executing distinct behaviors (such as AddTransaction or GetTransactionHistory). Ports (Interfaces): Inverted boundary abstractions defining data access contracts without identifying the underlying storage technology.

🗄️ III. Infrastructure Layer (Adapters & Persistence)

Handles concrete technical details.

  • Repository Implementations: Fulfills the Application Layer ports by writing direct, optimized asynchronous routines against IndexedDB database stores.
  • Performance Optimizations: Leverages customized IndexedDB indexes matching query filters (accountId, categoryId, date) to maintain a strict performance ceiling of sub-100ms response times.
import { Result } from '@/shared/types/result';
import {
  SaveError,
  NotFoundError,
  QueryError,
  DeleteError,
  UpdateError,
} from '@/shared/types/errors';
import { Transaction, TransactionData } from '@/domain/entities/transaction';

export interface TransactionFilters {
  accountId?: string;
  categoryId?: string;
  type?: 'income' | 'expense' | 'transfer';
  startDate?: string;
  endDate?: string;
  minAmount?: number;
  maxAmount?: number;
  searchText?: string;
  tags?: string[];
}

export interface TransactionRepository {
  save(transaction: Transaction): Promise<Result<Transaction, SaveError>>;

  findById(id: string): Promise<Result<Transaction, NotFoundError>>;

  findAll(filters?: TransactionFilters): Promise<Result<Transaction[], QueryError>>;

  findByAccountId(accountId: string): Promise<Result<Transaction[], QueryError>>;

  findByCategoryId(categoryId: string): Promise<Result<Transaction[], QueryError>>;

  findByDateRange(startDate: string, endDate: string): Promise<Result<Transaction[], QueryError>>;

  update(transaction: Transaction): Promise<Result<Transaction, UpdateError>>;

  delete(id: string): Promise<Result<void, DeleteError>>;

  deleteByAccountId(accountId: string): Promise<Result<void, DeleteError>>;

  count(filters?: TransactionFilters): Promise<Result<number, QueryError>>;
}

// Made with Bob
Enter fullscreen mode Exit fullscreen mode

🎨 IV. Presentation Layer (UI Workspace)

The responsive web interface layer.

  • Technology Stack: Engineered with React functional components compiled via Vite and styled responsively using Tailwind CSS. Custom Hooks & Component Composition: Isolate UI presentation layout from business lifecycle operations. React components talk exclusively to specialized hooks (like useTransactions), which wrap state management and instantiate the underlying Application use cases.

Strict Testing Strategy

To defend the application against regressions, the codebase mandates a minimum of 80% line coverage on all business logic components, verified incrementally via Vitest:

  • Domain & Application Validation: Covered near 100% using isolated unit tests verifying constructor invariants, math calculations, and explicit scenario outcomes.
  • Use-Case Integration Hooks: Validates the end-to-end pipeline by executing use cases against automated, transactional in-memory test databases.


Conclusion

Ultimately, adopting Spec-Driven Development (SDD) transforms software engineering from a series of unpredictable, ad-hoc prompt iterations into a rigorous, industrialized discipline where structured specifications remain the absolute source of truth. By decoupling what is being built from how it is executed, SDD eliminates developer guesswork, neutralizes AI hallucinations, and provides complete traceability from foundational project principles straight through to the production codebase. Navigating this highly structured methodology can introduce administrative overhead, but this is exactly where an AI lifecycle engine like IBM Bob SDLC becomes a force multiplier. By natively acting as an automated workflow companion, Bob rapidly accelerates development — seamlessly scaffolding markdown documents, running quality validation checks, and executing atomic tasks in a controlled, test-driven pipeline. Combining the structural integrity of GitHub’s Spec-Kit with the precision execution of IBM Bob allows technical teams to build complex, privacy-first applications with unprecedented velocity, proving that the fastest way to write enterprise-grade code is to design it right first.

>>> Thanks for reading <<<

Links

Top comments (0)