AI & ML
Experimentando "Spec-Driven-Development"
Rodolpho Alves Dev.to (EN Zone)
1 views
Contexto
Antes de se aprofundar nos pareceres de "beber da água" da IA alguns pontos úteis.
Essa será uma série de posts acompanhando o desenvolvimento de um IDP Open Source com objetivo de ser o mais play n' play possível.
Código Fonte
O código fonte, exemplos e specs para essa série ficarão disponíveis no repositório do Curupira Core:
rodolphocastro
/
curupira-core
The poor man's Internal Developer Platform!
Curupira Core
Curupira aims to be the "poor man's Backstage.io" – an IDP (internal developer portal) for your organization that's
straightforward to start and maintain.
If Backstage.io feels like too much for your organization, Curupira might be a good alternative! Especially compared to
rolling your own "IDP with Excel / Google Sheets" (been there, done that).
Why "Curupira"?
Curupira comes from Brazilian folklore.
While the original Curupira is the guardian to the forest, this Curupira is the guardin to your tech stack!
Honestly? I just wanted to find a cool folklore-based name for a project to honor my home country's culture!
Main Features
Single box setup
Getting Curupira up and running is as simple as doing docker start and following the instructions on the Webpage.
Maintenance overhead is kept minimal by using PocketBase as Database, Auth Provider and File Storage.
After booting your instance for the first time, you…
View on GitHub
Sobre "IA" e Ferramentas para Desenvolvimento
Quem convive comigo sabe que sou cético quanto ao "great replacement" de desenvolvedores / QAs / DevOps pelos "agentes de IA".
Além disso chamar o que temos, hoje, de "Inteligência" é forçar a barra, por mais que os milhões de dólares investidos coletivamente em marketing das big techs tentem fazer colar.
Mas concordo com um fato pois o acompanho desde o início da minha carreira: o LLM é uma ferramenta útil para apoiar a esteira de desenvolvimento. Isso é verdade desde o começo com o advento dos AutoCompletes em IDEs mais fortes, passando pelo Intellisense, sua versão "IA" pré-pandemia e agora com os CoPilot / Cursor e Claude Code da vida.
O "X" da questão é: como usar de uma maneira que ainda seja engenharia de Software e não pedreiragem de software?
O que é "Spec" Driven Development
Essa definição ainda está se moldando conforme você, leitor, aprecia esse post aleatório no dev.to.
Dependendo quem estiver tentando te vender a solução você irá encontrar diferentes definições e 'sabores', por exemplo:
Kiro Code (Amazon) que cita o fluxo de requirement > design > task
Junie (JetBrains) que parece o mais lean de todos os modelos, apenas um markdown e uma lista de tasks se quiser
GitHub (Microsoft) que leva ao modelo "SpecKit" bem mais amplo
Também existem artigos e livros começando a surgir, porém não me aprofundei ainda em nenhum deles pois muitas reviews são negativas citando AI Slop.
Olhando para todos esses sabores chegamos em um fluxo bem similar ao SDLC comum:
Alguém define o que precisa ser feito (Coleta e requisitos e escreve a Story, ou Spec)
Alguém planeja o que precisa ser feito, em alto nível (Refina a Story, ou Planeja a Spec)
Alguém executa o que precisa ser feito
Com isso em mente... vamos experimentar.
O Experimento
Vou utilizar o Claude Code como principal ferramenta pois é a que melhor se integrou ao meu fluxo de trabalho, parecendo uma jornada mais amigável ao "AI-Assisted" do que uma jornada full vibe coding.
A feature que quero desenvolver como exemplo é o comportamento de Bootstrap do Curupira, que é a porta de entrada esse IDP:
O sistema deve "bootar" o backend, um servidor do PocketBase
Deve automaticamente aplicar migrations, se precisar
Deve servir o frontend, que estará "buildado" em uma pasta específica
Se tudo isso funcionar o que eu espero é que o usuário, ao navegar para o IP, veja a home do sistema.
Importante: por ser a primeira feature a home é basicamente o "Hello, World" do framework de frontend.
Guardrails para o modelo
Com base no "destilado" de todos os artigos decidi que iria seguir guardrails similares ao que faria em um projeto com outros desenvolvedores:
Capturar decisões relevantes com ADRs (em markdown) e comentários no código
Escrever as Stories sempre pensando no: "Why and What", deixando claro o propósito além dos critérios de aceite para o que está sendo feito
Para os "critérios" de aceite decidi utilizar a sintaxe Gherkin pois a acho uma das mais práticas para transmitir contextos não técnicos
Side-Note: Sempre que uso Gherkin acho irônico meu viés de preferir fazer TDD a fazer BDD. Todo a infraestrutura adicionar de transformar Gherkin em testes E2E sempre me pareceu mais dor de cabeça do que garantir uma boa coverage com TDD e alguns E2E automatizados para o todo.
Principal diferença aqui é que as stories estarão dentro do repositório, se chamaram "specs" e serão escritas em Markdown.
De maneira visual todo esse "overhead" ficará assim:
Instruções Principais
CLAUDE.md (ou AGENTS.md) é o "index" para levar a outros arquivos de contexto, definir as convenções e as instruções da ferramenta.
Aqui, por exemplo, defini para que nesse experimento seja seguido as melhores práticas de:
Sempre fazer lint e format check
Utilizar Conventional Commits, atômicos
Principais comandos e pastas do projeto
Nunca fazer push, merges ou PRs por conta própria
Seguir o que estiver definido em ADRs e Specs, fazendo perguntas se precisar
Exemplo:
## Code style
- Formatting is owned by **oxfmt**, not Prettier: no semicolons, single quotes (`.oxfmtrc.json`). ESLint's Prettier config only disables conflicting style rules — don't hand-format against Prettier defaults.
- `oxlint` handles most linting (correctness rules across `eslint`/`typescript`/`unicorn`/`oxc`/`vue`/`vitest` plugins); ESLint is layered on top mainly for Vue/TS/Playwright/Vitest-specific rules oxlint doesn't cover.
- 2-space indent, LF endings, max line length 100 (`.editorconfig`).
## PocketBase backend (`backend/`)
- The `pocketbase` binary is **never committed** (gitignored) and there's no fetch script yet — it must be placed manually at `backend/pocketbase` (download from pocketbase.io). Don't assume it's present.
- `backend/pb_public/` (build output) and `backend/pb_data/` are also gitignored and may contain stale artifacts from a previous build — don't treat their presence as evidence of a working build.
- Hooks go in `backend/pb_hooks/`, migrations in `backend/pb_migrations/`.
- No CI/CD exists yet to fetch/pin the binary or deploy — this is a known open gap (ADR-001), not an oversight to silently fix.
- No env var scheme is defined yet for the frontend to reach PocketBase (no `.env.example`). Flag this rather than inventing a convention.
## `docs/` practices
`docs/decisions/` holds ADRs (why something was built a given way); `docs/specs/` holds Specs (current intent for a module/feature, not a changelog). File names are kebab-case.
- Before refactoring or adding something new, check `docs/decisions/` for constraints.
- Write a new ADR for architecturally-relevant decisions (test framework, database/broker choice, project structure, breaking API/data-contract changes). If a change breaks an existing ADR, add a new one and mark the old one's status `Superseded by ...`.
- Use `docs/decisions/000-template.md` and `docs/specs/template.md` as the starting point.
- A Spec's "Open Questions" section means stop and ask — don't assume an answer.
## Git / commit conventions
- Use Conventional Commits.
- Always work on a feature branch — never commit directly to `master`.
- Never open a pull request or issue by yourself, this is always done by the human.
ADRs
Primeiro: ADRs são "Architectural Decision Records" - praticamente um "diário" ou "logs" de decisões relevantes para a arquitetura de um projeto.
Elas podem ser capturadas em qualquer ferramenta, até mesmo em comentários, mas eu costumo utilizar Markdown para elas - o que é uma mão na roda pensando em "Spec Driven".
Para esse projeto optei por usar uma versão minimalista do mADR:
adr
/
madr
Markdown Architectural Decision Records
Markdown Architectural Decision Records
"Markdown Architectural Decision Records" (MADR) [ˈmæɾɚ] – decisions that matter [ˈmæɾɚ].
For user documentation, please head to https://adr.github.io/madr/.
Quick start
adr-template.md has all sections, with explanations about them.
adr-template-minimal.md only contains mandatory sections, with explanations about them.
adr-template-bare.md has all sections, which are empty (no explanations).
adr-template-bare-minimal.md has the mandatory sections, without explanations.
Copy it into docs/decisions
For each ADR, copy the template to nnnn-title.md and adapt.
Longer explanation: Head to https://adr.github.io/madr/#applying-madr-to-your-project.
Development hints
MADR follows Semantic Versioning 2.0.0 and documents changes in a CHANGELOG.md following keep a changelog 1.0.0.
Issues can be reported at https://github.com/adr/madr/issues.
Suggestions can be contributed via pull requests. MADR offers pre-configured VS Code web environment at Gitpod.
MADR uses markdownlint as Linter for Markdown files. Use markdownlint for checking for linting issues in VS Code.
template/adr-template.md is mirrored to docs/decisions/adr-template
However, following YAML…
View on GitHub
Exemplo minimalista:
<!-- Actual text should replace stuff within {{}} when writing the ADR -->
# ADR-{{Number}}: {{Short name}}
Date: {{ISO-8601 Based Date on which this was decided}}
Status: {{Either Proposed, Accepted or Superseded by ... }}
## Context
<!-- What problem forced this decision? What contraints mattered the most? -->
## Decision
<!-- What we're doing to tackle this, stated plainly -->
## Consequences
<!-- What are the consequences of this decision? What does this decision make easier, harder or its tradeoffs -->
## Additional Information
<!-- Any other information that might be useful to understand this decision -->
Specs
As specs em si também serão arquivos markdown contendo o contexto daquela feature, os cenários em Gherkin e questões em aberto (como não temos um backlog).
Como não achei nenhum padrão 'enxuto' o suficiente acabei fazendo o meu próprio para esse projeto:
<!-- Actual text should replace stuff within {{}} when writing the spec -->
# Spec: {{Feature/Module Name}}
Status: {{Draft, Active or Deprecated}}
## Purpose
<!-- What does this do, and who or what relies on it? One or two sentences -->
## Behavior
<!-- Concrete, testable statements of what happens. Inputs, outputs, edge cases that matter. Bullet list, not prose -->
## Out of Scope
<!-- What this deliberately does not do, to keep scope creep visible -->
## Open Questions
<!-- Anything still undecided. An agent should stop and ask rather than assume here. Delete a line once it's answered -->
Test-Run
Com todos os guardrails no lugar escrevi a primeira ADR pois não ia deixar a critério do Claude escolher a tech stack. Nela defini que utilizaria Vue, como seria o deploy e como o PocketBase era o backend daquele projeto:
# ADR-001: Adopt PocketBase as the backing service.
Date: 2026-07-28
Status: Accepted
## Context
Curupira's (name of this project) Core should be a dead-simple deployment that spins up in less than 10 minutes.
It should still be able to support everything an IDP (Internal Developer Portal) needs, but its core should be `#simple`.
Extensibility should be allowed but won't be our driving feature for adoption, we're meant to be a poor man's version
of backstage that allows teams with simpler stacks to get up and running quickly on their IDP journey.
Given that I'm the solo developer on this, I would also like to keep this as close as possible to my favorite tech stack,
which is TypeScript and Vue for frontend itself.
Quality matters so tests must be a first-class citizen.
## Decision
To keep things simple and aligned to our current context, we'll be adopting the following tech-stack:
- Vue for the web-portal with its main plugins:
- Router
- Pinia
- Pocketbase for Authentication, Database, File Storage and Serving the Frontend
- Playwright for End-to-End Testing
- Vitest for Unit Testing
Deployment will eventually be handled by getting a `pocketbase` binary into the `/backend` directory.
Extensibility should be done by satellite applications writing records into `pocketbase` through its REST API, and we'll
adopt PocketBase's conventions to keep it up and running.
## Consequences
We need to figure out a way to properly get a `pocketbase` binary into the `/backend` directory as part of CI/CD.
Extensibility will be done by satellite applications writing records into `pocketbase` through its REST API.
Eventually we'll need to figure out a way to have extensions toggle based on their satellite / companion being connected
or not with the `extensions` collection on pocketbase.
The `backend/` directory needs to be properly configured to ensure no binaries ever get checked into Git.
We're also going to miss out on better search experiences, but we'll have to live with that for now.
## Additional Information
For comparison, these are the instructions to get [Backstage deployed in production](https://backstage.io/docs/deployment/).
Pocketbase's own documentation is available on [pocketbase.io](https://pocketbase.io/docs).
A sample project to look at is [pocket-chat](https://github.com/PocketTogether/pocket-chat)
Outro ponto importante: defini que não seriam utilizados os hooks do PocketBase. Extensibilidade para esse projeto (por enquanto) deve ser feita através das APIs nativas do PocketBase.
Com a ADR definida parti para a primeira spec, onde perdi maior parte do tempo, que define o comportamento esperado do sistema com o boot.
# Spec: System Boot
Status: Accepted
## Purpose
The system should be simple to boot but guarantee it is in a valid state and ready to be accessed by its users and extensions.
That means we need to assert that:
- Important `collections` are created in the database
- The appropriate files for hooks are present, if any
- The appropriate frontend bundle is present
## Behavior
Important:
- PocketBase is the underlying database and backend infrastructure
- Curupira is the "bundle" of PocketBase plus our own Frontend and its logics
So 'starting' Curupira means that PocketBase is also started as it is the backing service.
Feature: Self Provisioning on Boot
Background:
Given PocketBase is not running
Scenario: Curupira starts up successfully
When Curupira is started
Then PocketBase should apply all its pending migrations
And PocketBase should serve the built-in frontend
Scenario: Curupira fails to boot due to invalid migrations
Given PocketBase has invalid collection data
When Curupira is started
Then an error log reading "migrations failed" along with the cause should be produced
## Out of Scope
- Guide the first user on setting up his instance (covered by [first user spec](first-user-creation.md))
## Open Questions
<!-- Anything still undecided. An agent should stop and ask rather than assume here. Delete a line once it's answered -->
- Is ensuring migrations are up enough?
- How to effectively restore the system if a migration happens to fail?
---
Essa spec garante:
Que exista um processo de migration
Que exista um teste que verifica que "sob condições normais" algo renderiza na web
Que existam logs, em algum lugar, caso migrations falhem
Com a ADR e a Spec pronta pedi para o Claude:
look at the spec that's defined on @docs/specs/system-boot.md
I would like you to implement it with proper e2e tests to validate the boot behavior.
Plan before you make any changes so I may review
E fiz a review do plano:
O resultado final pode ser visto nesta PR, onde acabei juntando com outra spec para ficar mais fácil o teste end-to-end:
feat: provision the system
#1
rodolphocastro
posted on Jul 30, 2026
This PR adds the "self-provision" flow which later will allow for the first user and default settings experience to take place.
View on GitHub
Como boa prática, antes de fazer merge, executei a suite de testes locais pnpm run test:unit; pnpm run test:e2e e manualmente verifiquei o fluxo através de um pnpm run dev e navegando até o bom e velho localhost.
Conclusões iniciais
Ironicamente o que mais me tomou tempo foi exatamente o que me demanda atenção em projetos normais: entender o que precisa ser feito, documentar e criar as "amarras" de qualidade para que o futuro não seja cheio de bugs.
Porém, dado que estou há alguns anos com pouca prática com Vue (infelizmente React dominou o mercado), recebi de bom grado o "apoio" de ter alguns arquivos de frontend gerados rapidamente.
As migrations foram os principais pontos de vai-e-volta durante o planning. Para as próximas ADRs e Specs anotei que preciso ser mais restritivo com a estrutura de dados, para que não vire uma bola de neve.
Curiosamente é o mesmo problema que enfrentamos em escala hah.
Com essa primeira feature fora do caminho agora é partir para as próximas features do Curupira!
Read original: https://dev.to/rodolpho_alves/experimentando-spec-driven-development-20ba
Related
ChatGPT Reaches 900 Million Weekly Users as OpenAI Expands AI at Global Scale
AI & ML
3
DEV Community
Four Debian 13 Boxes, One Brief: 1,923 Packages on Metal, 328 in the Cloud
AI & ML
1
DEV Community
Earned Automony
AI & ML
0
Dev.to (EN Zone)
Local models are actually good now - playing with Qwen3.8-27B
AI & ML
0
Dev.to (EN Zone)
Comments0
No comments yet — be the first