Skip to main content
Building with AI (Vibe Coding)

⏱ About 20 min20 XP

Choosing Tools and Constraints

Every project requires decisions: which programming language, which framework, which database, which hosting platform. These decisions feel technical, but they are architectural — they shape everything that follows. A poorly chosen tool can add weeks of friction to a project; a well-chosen one can make hard problems disappear. Learning how to reason through these choices, and how to set deliberate constraints around them, is a skill that distinguishes effective builders from perpetual experimenters.

How to Evaluate a Tool

Tool selection involves evaluating candidates across several dimensions. Rushing through these dimensions leads to regret at the worst possible time — when you are deep in implementation and discover a fundamental mismatch. Fit for purpose: Does the tool solve your actual problem? A document database like MongoDB fits naturally when your data is hierarchical and schema-flexible. A relational database like PostgreSQL fits when your data has clear relationships and you need reliable transactions. Using MongoDB because it feels modern — when your data is highly relational — is a category error. Learning curve vs. project timeline: Every tool has a learning curve. A powerful but unfamiliar framework may produce better long-term outcomes while costing you the first week in orientation. For a four-week student project, a tool you already know at a medium level will usually outperform a tool you are learning from scratch. Reserve unfamiliar tools for projects where learning is the explicit goal. Ecosystem and documentation quality: A tool with poor documentation is a tax on every task. A tool with an active ecosystem means that when you hit a difficult problem, solutions exist online. Check: Does the tool have current documentation? Are there active questions and answers on forums? When was the last release? Long-term maintenance: In professional settings you think about who maintains the tool and whether the project is likely to be alive in three years. For student projects this matters less, but it is worth noting that a tool with a single maintainer and no recent commits is a risk. Compatibility: Do your chosen tools work together? If your front-end framework assumes a REST API and your back-end produces GraphQL, you have a compatibility gap to bridge. If your chosen database driver does not support the runtime you are using, you may need to switch one or the other. Example: A student is building a real-time multiplayer quiz game. Candidates for the back end include: a REST API (simple, well-understood, but requires polling for real-time updates), a WebSocket server (native real-time but more complex to manage), or a managed real-time service like Supabase Realtime or Firebase (real-time built in, less infrastructure to manage). For a student project on a four-week timeline, the managed real-time service offers the best fit-for-purpose vs. learning-curve trade-off.

Choose Boring Technology for Reliable Delivery

'Boring technology' is a phrase in the software industry for tools that are mature, well-understood, and have solved their major problems. Boring tools have better documentation, more community answers to problems, and fewer surprises. Choose boring when delivery matters. Choose exciting when learning is the goal — and be explicit about which you are doing.

Constraints are restrictions you impose deliberately to make decision-making easier and outputs more consistent. A constraint is a rule: "This project uses TypeScript only — no plain JavaScript." "All database access goes through the data-access layer — no direct SQL in components." "Third-party UI component libraries are prohibited — all UI is custom." Constraints feel limiting, but they are liberating in practice. Every unconstrained decision is a source of inconsistency. If any component can access the database directly, you cannot audit data access. If some files are TypeScript and others are JavaScript, you lose the type-safety guarantee everywhere. Constraints create predictability. For AI-assisted building, constraints are especially powerful. An AI model, given an open-ended prompt, will make its own choices: it may use a library you did not intend, generate code in a style that conflicts with your existing work, or introduce a dependency that creates a conflict. Explicit constraints in your prompts prevent this: "Use only the libraries listed in package.json. Do not introduce any new imports. Follow the existing naming conventions in the file." Constrained prompts produce more integrable outputs. Common categories of constraints to define before building: Language constraints: which language and version. Dependency constraints: which libraries are permitted; whether to avoid adding new ones. Style constraints: naming conventions, file structure, coding patterns. Architectural constraints: which components may call which; where business logic may live. Security constraints: which endpoints require authentication; which data may be exposed to which roles.

Complete each statement.

Choosing a tool that is mature and well-understood rather than new and exciting is sometimes called choosing technology. A deliberate restriction that makes decision-making more consistent and AI outputs more predictable is called a .

Making and Recording Tool Decisions

Tool decisions should be recorded. Decisions made in your head and not written down get lost. When you return to a project after a break, when a team member joins, when you write a prompt for an AI assistant — in all these situations, an unrecorded decision must be re-discovered or re-made, which introduces inconsistency. A simple Architecture Decision Record (ADR) is a lightweight format for capturing decisions. Each ADR answers three questions: What decision was made? Why was it made (what options were considered and what drove the choice)? What are the consequences (what does this decision enable, and what does it rule out)? Example ADR: Decision: Use PostgreSQL via Supabase as the database. Why: The project data is highly relational (users, courses, assignments, submissions with clear foreign-key relationships). PostgreSQL's row-level security allows us to enforce per-user access policies at the database level, removing a class of security errors from the application layer. Supabase provides a managed instance with a built-in REST API, reducing infrastructure setup time. Options considered: Firebase (document model, poor fit for relational data), SQLite (no managed hosting, not suitable for multi-user access). Consequences: All data access uses the Supabase client library. No raw SQL outside the data-access module. Schema changes require migrations stored in the /supabase/migrations directory. Three ADRs covering language, database, and framework choices will serve any student project well. They take 20 minutes to write and save hours of confusion.

Avoid Tool Churn

Switching tools mid-project — changing databases, switching frameworks, replacing the back-end language — is extremely expensive. It typically requires rewriting substantial portions of already-completed work. Make tool decisions deliberately and early, record them, and hold to them unless a genuinely blocking problem is discovered.

A student is building a personal portfolio website with a contact form and a list of projects. They consider using a microservices architecture with Kubernetes because it is used at large tech companies. What is wrong with this reasoning?

Why do explicit constraints in AI prompts produce more reliable code outputs?

Tool Decision Records

  1. Step 1: For your running project, identify five tool decisions you need to make: programming language, front-end framework or approach, back-end approach (if applicable), database or storage, and one other library for a key feature.
  2. Step 2: For each decision, list at least two alternatives you considered.
  3. Step 3: Write a one-paragraph ADR for each decision: what you chose, why you chose it over the alternatives, and what constraints this decision imposes on the rest of the project.
  4. Step 4: Write a constraints list: at least five explicit rules your project will follow (e.g., 'All API calls from the front end go through a single api.js module,' 'No inline styles — all CSS uses the project's design tokens').
  5. Step 5: Review: does any constraint conflict with another? Does any tool choice conflict with another? Resolve any conflicts before you proceed to building.