Christian Ledermann: Buzzword Bingo: An Experiment in Spec-Driven AI Development
This is a submission for Weekend Challenge: Passion Edition What I Built I built Buzzword Bingo, a multiplayer bingo game for conferences, webinars and meetings where players mark off the inevitable buzzwords as they appear. The application allows someone to create a game, share a link with participants, and let everyone play along on their own unique bingo board. The first player to complete a row, column or diagonal wins. Under the hood, though, the game itself was almost secondary. The real goal was to answer a question I had been wondering about for a while: How far can I push Claude with specification-driven development while still achieving reliable type coverage and maintaining the coding standards I expect from a production Python project? The project became an experiment in AI-assisted software engineering, strict typing, and how much guidance modern coding agents actually need to produce maintainable software. Demo Live demo coming soon. Repository: bsbingo on GitHub Code Repository: bsbingo GitHub repository How I Built It Specification Driven Development The project followed a specification-driven approach using Speckit. Rather than iterating directly in code, I created specifications describing what the system should do and allowed Claude to implement them. A big accelerator for the project was using scaf for the initial bootstrap. Rather than spending the first few hours wiring together repository structure, CI, containerization, infrastructure, and developer tooling, I started from a production-oriented foundation and focused on shaping it to match my own preferences. Having Kubernetes manifests, Terraform, deployment pipelines, and modern Python tooling available from day one made it much easier to concentrate on the actual experiment: how far specification-driven development and AI coding agents could take the application. I ended up needing three major specifications: Project scaffolding Starting from a project generated with scaf. Refining the generated structure to match my personal preferences. Adding all the infrastructure and tooling I typically expect in a modern project. Backend implementation Django models and business logic. Server-rendered templates. HTMX interactions. Capability URL based authorization. Frontend implementation Visual styling and user experience. Responsive layouts. End-to-end testing using Playwright. Django Without the JavaScript Framework The application uses: Django HTMX Django templates PostgreSQL HTMX turned out to be an excellent fit for this type of application. Most interactions consist of: clicking a square, sending a POST request, returning an updated HTML fragment, swapping it into the page. No client-side state management was required. Capability URLs One design decision I particularly liked was using capability URLs instead of authentication. Each board receives a unique UUID: /board/5b97b663-1f2f-4e54-8d2f-f45f3272f870/ Possession of the URL grants access to that board. This removes the need for: user accounts, sessions, authentication, authorization logic. For a lightweight conference game this felt like the right trade-off. Going All-In On Type Safety I care a lot about clean code and strong typing in Python, so I decided to push the type system as far as possible. Instead of relying on a single type checker, I combined: ty zuban pyrefly This was paired with a strict ruff configuration with almost every rule enabled. One of the goals of the experiment was to see whether Claude could operate effectively within these constraints. What Worked This instruction worked surprisingly well: Prefer precise, narrow types (Enum, NewType, TypedDict, dataclasses with Final or Literal fields) over Any, untyped dict or list, or stringly-typed values. Illegal states should be unrepresentable in the type system rather than guarded against only at runtime. Once Claude had a few examples to follow, it started producing significantly better type annotations and more expressive domain models. Pre-commit hooks proved to be the first line of defence, catching issues before they ever reached CI. Linters, formatters, and all three type checkers ran automatically on every commit, providing rapid feedback and keeping the codebase consistent throughout the experiment. To avoid spending time hand-crafting the configuration, I used pc-init to generate a strict .pre-commit-config.yaml tailored for modern Python projects. This ensured that formatting, linting, and type checking became part of the development workflow rather than an afterthought. What Didn't Work Claude struggled with this instruction: All Python code MUST be fully type-annotated; untyped function signatures and untyped module-level values are not permitted. Instead of fixing missing annotations, it occasionally attempted to disable checks in pyproject.toml. Some manual intervention and code review were required to steer it back towards the desired standards. The experience reinforced an observation I've made repeatedly with coding agents: Agents optimize for making the error disappear, not necessarily for preserving your engineering constraints. If you care about those constraints, you still need strong feedback loops. Type Checker Observations Running all three type checkers together was still faster than a single mypy run. Interestingly, they complemented each other rather than duplicating effort: ty found some issues the others missed. pyrefly found different classes of problems. zuban felt the closest to mypy and was by far the easiest to configure. The newer type-checking ecosystem is still catching up with mypy in terms of documentation and examples, so reaching the level of strictness I wanted involved a fair amount of experimentation. Prize Categories Not submitting for any specific prize category. The real prize was finding out how far AI-assisted, specification-driven development can be pushed before human review becomes the limiting factor.
1 day ago