# CodeWalkers — Complete Content This file contains the full text of all published articles on codewalkers.com. It exists so LLMs and AI search engines can ingest the entire site in one fetch. Articles are organized by cluster and sub-section. Each article begins with a level-2 heading containing the article title, followed by the canonical URL, then the article body in markdown. --- # AI Coding Tools ## Complete Guide to Google Antigravity (2026) URL: /ai-coding-tools/antigravity/ Cluster: AI Coding Tools > Overview & AI Coding Tools Google Antigravity now has two related surfaces: an agentic IDE for direct editing and Antigravity 2.0, a standalone desktop command centre for running and monitoring agents. Google documents agents that can work across the editor, terminal, and browser, with artifacts for reviewing the result. I have used Antigravity, and I don't like it enough to make it my main coding tool. My problem is the supervision it needs when a small request turns into a larger change. The example below explains why I still prefer Cursor. ## What Is Google Antigravity? Google Antigravity is a family of AI development tools that lets agents inspect projects, edit code, run terminal commands, and use a browser. The Antigravity App coordinates agent tasks; the VS Code-derived Antigravity IDE combines agent work with direct code editing. The family also includes a CLI, editor extensions, and an SDK. Start by deciding whether you need an editor or a place to supervise longer-running tasks. The useful distinction is how you plan, inspect, and review the work. Parallel agents are also available in competing tools, so a dashboard alone does not establish an advantage. ## Getting Started Choose the Antigravity App or IDE from Google's official download page, sign in, and open a small project you already understand. Check its permissions and review settings before asking it to make changes. 1. Ask the agent to locate one feature and explain the files involved without editing. 2. Request a small change, such as a loading state for one button, with an explicit file boundary. 3. Ask for the proposed approach and the check that will prove it works. 4. Review the diff and any browser artifacts, then run the project's own checks. The IDE inherits many VS Code conventions, but the standalone App is a different surface. Use the shortcut list and settings for the product you installed instead of assuming every command works in both. ## Manager View Antigravity's agent-management interface organizes active tasks so you can inspect what each agent is doing and review its output. It is useful when several distinct jobs need supervision. Compare that experience with the multi-agent workflows in other tools using the same tasks. Parallel work helps only when the tasks do not overlap. A database migration and a UI polish pass may be separate; two agents rewriting the same component are not. Keep each task in a separate scope, review the artifacts and diff, then run the project's own checks. More autonomy means more output to inspect, not less responsibility. ## The Models You Can Run Model access depends on the Antigravity surface and your plan. Google's [current model table](https://antigravity.google/docs/models) lists Gemini 4 Argon for eligible paid accounts in the App, CLI, and extensions, but not Antigravity IDE. Gemini 3.8 Flash has broader access, while Claude 5.5 models require eligible paid plans. Google lists November 2, 2026 as the removal date for its older Claude 4.6 and GPT-OSS options. Check the live picker before committing a workflow to one of those models. Model availability and quota are separate questions. The model menu is useful only when it changes a result you can review. Run the same representative task through the models available to your account, then record elapsed time, output quality, and usage. Do not pick a plan from a model name alone. ## Skills and Rules The first time I let Antigravity's agent loose on a real task, I asked it to add a loading state to a button and it restructured my entire component folder instead. It moved files, renamed things, and introduced a folder layout I never asked for. The code worked, the architecture was arguably better, and it was still nowhere near what I'd actually requested. That's the core problem with agents that have judgment but no guardrails: their preferences are reasonable, they're just not necessarily yours. Rules give an agent project instructions; skills package task-specific procedures; MCP connects it to additional tools. These mechanisms solve different problems. None replaces permissions or review. Google documents plain `AGENTS.md` or `GEMINI.md` files for directory-scoped instructions. Modular rules belong in `.agents/rules/*.md` and require valid `trigger` frontmatter; the legacy `.agent/rules` directory is also supported. Save this plain example as `GEMINI.md`: ```markdown # Project Rules ## Framework Preferences - Use React for UI components - Use Tailwind CSS for styling (no CSS-in-JS) - Use Zod for validation ## Constraints - Do not add new dependencies without asking first - Do not restructure existing folder organization - Do not create abstraction layers unless explicitly requested ``` The headings aren't fixed; agents parse the content as context, and the more specific you are, the more consistently they follow it. A few patterns earn their keep in daily use. Framework consistency stops the worst mid-project surprises, the agent reaching for styled-components in one file and Tailwind in the next. Guardrails against over-engineering matter more than people expect, which I think of as the black coffee principle: if I ask for coffee, don't assume I want milk, sugar, and sprinkles, so "start simple" and "don't add abstractions without asking" pull real weight. File-organization rules head off the restructuring mess I described above. Both extremes cause real problems in practice. A rule like "write good code" constrains nothing; micromanaging rules that try to script every decision leave the agent no room to operate. "Use early returns instead of nested conditionals" gives an agent something concrete to act on. Skills contain a `SKILL.md` file and optional supporting resources. Antigravity can select a relevant skill from its description, and the App also supports explicit invocation with `/`. Automatic selection does not mean a skill is guaranteed to load for every matching task. When adapting instructions from another tool, check the file location and activation format. Cursor project rules use `.cursor/rules/*.mdc`, Claude Code uses `CLAUDE.md`, and Antigravity supports `AGENTS.md`, `GEMINI.md`, and modular rules. Keep shared conventions consistent without assuming the configuration formats are interchangeable. One caution is worth flagging on the Armoury side. The more MCP connections you wire up, the more your agents can reach, and that surface is genuinely powerful. Shortly after launch there was a reported persistent code-execution vulnerability involving malicious workspace rules that abused this access. I treat MCP permissions the way I treat any other credential: connect what a task needs and nothing I can't account for. ## Google AI Ecosystem Antigravity doesn't exist on its own in Google's lineup. Google has shipped a confusing pile of AI developer tools in a short span, and knowing where Antigravity sits saves you from picking the wrong one. Antigravity is the desktop, agent-first surface, and the others occupy different lanes. Gemini Code Assist is the plugin model, dropping completions and an Agent Mode into VS Code, JetBrains, and Android Studio rather than asking you to switch editors. It's Google's enterprise-facing answer to [Copilot](/ai-coding-tools/copilot/); its IDE extensions remain available to Gemini Code Assist Standard and Enterprise licensees. Antigravity CLI brings Gemini to the terminal with natural-language commands and the same agent harness as Antigravity 2.0. It replaced Gemini CLI as the free consumer option when Google stopped serving Gemini CLI to free, Google AI Pro, and Ultra users on June 18, 2026. Gemini CLI remains available through enterprise licenses and paid API keys. Jules is the asynchronous one: it connects to GitHub, clones your repo into a secure Google Cloud VM, works in the background while you close the browser, and hands back a pull request when it's done, with tiered limits that scale across Google's AI plans. Firebase Studio is the cloud-IDE lane for Firebase-centric prototyping, and it has carried preview caveats, so I'd keep a backup plan before standardizing on it. The overlap is real, and Google hasn't always explained when to choose which. In practice, if you want Gemini in your current editor, use Gemini Code Assist; if you want background tasks delivered as PRs, use Jules; if you want to supervise several agents from a dashboard and you're curious about the agent-first workflow, that's Antigravity. All of these run Gemini underneath, which is the throughline, and it's also why Google can afford to keep several of them alive at once. ## How Much Does Antigravity Cost? Antigravity has a quota-limited free tier and paid access through Google AI plans. Google's May 2026 announcement priced Pro at $20 per month and the two Ultra tiers at $100 and $200; confirm current local pricing before subscribing. [Google's May 19 plan update](https://antigravity.google/blog/changes-to-antigravity-plans) set the current individual tiers below, while the live plans page explains how their quotas refresh. Check both before committing a budget because model access and capacity language have already moved since launch. | Plan | Price | Usage limit | | Free | $0 | Rate-limited; quota refreshes weekly | | AI Pro | $20/month | Higher weekly limit; quota refreshes every five hours until the weekly cap | | AI Ultra 5x | $100/month | Five times the Pro token allowance | | AI Ultra 20x | $200/month | Twenty times the Pro token allowance | AI Pro and Ultra users can enable purchased AI credits after the baseline quota runs out. Google consumes those credits at Gemini Enterprise Agent Platform pricing, and the setting can wait for the next refresh or allow overages automatically. Third-party models use a separate fixed quota where available; Antigravity's plans page explicitly says there is no bring-your-own-key route for extra limits. Team and enterprise access runs through Google's Gemini Enterprise Agent Platform. Both Antigravity and Cursor combine subscriptions with usage limits or additional charges. Cursor's current Plus and Premium plans cost $8 and $30 per month, with different model and agent access; the older $20 Pro price is a legacy plan. Compare the features and quota needed for your actual tasks. My take: the AI Pro tier at $20/month is the right starting point if you're evaluating seriously. Try the free quota first, then use Pro if its limits interrupt a useful evaluation. The Ultra tiers only make sense after you have validated the workflow on your own tasks. ## Limits and Fit I like the idea and I keep it installed, but my experience has not persuaded me to replace Cursor with it. The product churns at a pace that's hard to track: features, limits, and pricing can change between when I write this and when you read it, so anything I peg too precisely will drift. The extension ecosystem is smaller than Cursor's; VS Code compatibility is partial, and every extension you depend on needs a real test. Google's enterprise route now runs through Gemini Enterprise Agent Platform, while the individual plans still use capacity-based limits and changing model availability. For a company evaluation, I would keep an established editor available and treat Antigravity as a measured pilot until its exact integrations and controls pass your requirements. There's also the Google factor to weigh. Antigravity is one of several AI developer products Google maintains at once, and Google has a long history of sunsetting tools, even popular ones, when interest moves elsewhere. The talent and the technology behind Antigravity are real, but whether Google commits to it for the long haul is genuinely unknown, including, I'd guess, to Google. So if you're a solo developer happy in Cursor, you don't need to switch, and Antigravity probably won't feel like a compelling primary tool. If you're coordinating a larger project with several distinct work streams, or you want heavy Gemini access tied to limits instead of API spend, spend a weekend with it and see whether the agent-team model solves a problem you didn't realize you had. Just keep your rules files portable and don't build anything irreversible on top of it yet. ## Compare Google Antigravity - [Cursor vs Google Antigravity](/ai-coding-tools/comparisons/cursor-vs-antigravity/): file-by-file editing against the agent-first, dashboard-driven model, and the most common comparison for developers already in Cursor The full set of [AI coding tools comparisons](/ai-coding-tools/comparisons/) covers the rest of the field, and the [best AI coding tools hub](/ai-coding-tools/) lists every tool we cover. ## Verdict Antigravity is worth a contained pilot when managing parallel agent tasks is the problem you need to solve. Start with one Project, a reversible task, and a review step that you already trust. Keep your existing editor when direct file work and one active task are the better fit. Compare it with [Cursor vs Google Antigravity](/ai-coding-tools/comparisons/cursor-vs-antigravity/) before turning it into a team standard. Updated September 23, 2026: explained Google's move of consumer accounts from Gemini CLI to Antigravity CLI, and that enterprise licenses and paid API keys still get Gemini CLI. --- ## Complete Guide to Claude Code (2026) URL: /ai-coding-tools/claude-code/ Cluster: AI Coding Tools > Overview & AI Coding Tools Claude Code is Anthropic's tool for asking Claude to work directly on a software project. It can read the project's files, propose edits, run checks, and explain what it changed. That makes it an AI coding agent: software that can take a series of actions toward a goal, instead of only answering one question at a time. The product began in a terminal (the text-only command window), which made early coverage sound much more technical than the tool now is. The easiest starting point in 2026 is the Claude Desktop app. You can open its Code tab, choose a folder, type a task in ordinary language, and inspect every proposed change without installing a command-line tool. | Claude Code | At a Glance | | Easiest start | Claude Desktop Code tab | | Terminal needed | No | | Works on | macOS, Windows; Linux beta | | Good first task | Explain or fix one known issue | | Starting price | $17/mo annual or $20 monthly | For our overall recommendations and the alternatives to Claude Code, see the [best AI coding tools](/ai-coding-tools/) guide. ## What Is Claude Code? Claude Code is an AI coding agent that can inspect a project, edit its files, and run commands to check its work. It works on a repository, often shortened to repo. A repository is the project folder that holds the code, supporting files, and a record of previous changes. If you have a small website in one folder, that folder may be the repository you select. You describe an outcome in a message. Claude Code reads relevant files, replies with an explanation or a plan, and can propose changes. For a request such as "the profile page shows an email where the display name should appear," it can locate the page, trace the data, change the responsible file, and run an existing check. The word agent matters because Claude Code can choose and repeat actions. It might search the repository, open two related files, edit one, run a test, and inspect the result. You still set the task and decide how much permission it gets. Claude Code is also different from an editor such as [Cursor](/ai-coding-tools/cursor/). Cursor surrounds you with a list of project files and offers suggestions while you type. Claude Code is built around a conversation and a task, although the desktop app now puts project files, command output, a running preview, and proposed changes around that conversation. ## How I Use It Claude Code is part of my daily stack. I keep it beside Cursor and use it as the control room for agent work. Cursor is where I make focused edits in place, while Claude Code gets the jobs that begin with an outcome and require several steps to finish. A typical request names the visible problem and the boundary around it: "The profile page shows the email where it should show the display name. Find the cause, fix it, and run the existing profile tests. Leave the database exactly as it is." That boundary gives the agent room to investigate without handing it the whole property. I always read every resulting change myself. An agent can move faster than a person and still misunderstand the request. The value comes from delegating the search and mechanical work while keeping the final judgement with the person who knows the project. ## Start with the Desktop App Anthropic's [desktop quickstart](https://code.claude.com/docs/en/desktop-quickstart) now gives beginners the shortest route into Claude Code: "No terminal required." The app includes Claude Code, so you do not need Node.js (a developer runtime) and you do not need to install the CLI separately. This route works on macOS and Windows, with a Linux beta for Ubuntu and Debian. You need a paid Claude plan that includes Claude Code and a project folder you are comfortable letting the app read. A suitable folder can be a class exercise, a personal website, or a copy of a small project whose result you can recognise. Before granting write access, duplicate that folder with Finder on macOS or File Explorer on Windows and select the copy. If your website exists only inside a hosted service and you do not have its files, stop here and use that service's export or repository instructions first. Claude Code needs a folder or a connected project to inspect. ### Install and Sign In 1. Download the Claude Desktop app for macOS, Windows, or Linux from Anthropic's desktop quickstart. 2. Install the app using the normal installer for your operating system. 3. Open Claude and sign in with the Anthropic account attached to your paid plan. 4. Click the Code tab at the top of the app. The app also has Chat and Cowork tabs. The Chat tab handles general conversations without project files. Cowork is a separate agent workspace for broader computer tasks, while Code is the part that opens a software project and tracks proposed code changes. ### Choose Where It Runs Select Local for the simplest first session. Local means Claude works with files on your computer. Click Select folder, then choose the project folder you want it to read. The other choices are Cloud and SSH. Cloud runs the session on Anthropic's infrastructure and can continue after you close the app. SSH connects to another computer, such as a server, and is an advanced route you can ignore during a first local trial. ### Pick a Model and Task A model is the underlying AI system that reads your request and produces the response. Leave the model picker on its existing selection for the first session. The default is chosen for your account, and changing models before you know what you need adds another decision without helping you learn the basic workflow. Type one task whose result you can recognise. These four requests make useful first trials: - "Explain what this small project does and name the main file." - "Find one TODO comment, explain it, and propose a fix." - "Change the heading on the home page from Welcome to My Portfolio." - "Find why the contact form accepts an empty email address, then show me the proposed change." The first example is read-only, so it helps you confirm that Claude opened the correct folder. The later examples ask for an edit you can see. Keep the first job small enough that you can tell whether the result matches the request. ### Review the Diff Check the permission selector next to the send button and pick Manual for your first task. Desktop remembers the mode selected for each folder, except Plan. In Manual mode, Claude waits for your approval before applying a proposed file change, and Anthropic's quickstart says the app shows a visual diff with Accept and Reject controls. A diff is a before-and-after view of changed text. Added lines and removed lines are shown separately, so you can inspect what Claude wants to alter. Accept a change when it matches the request, or reject it and tell Claude what needs to be different. Your files remain unchanged until you accept in Manual mode. That makes Manual a good first setting. You can learn how Claude reasons about the project while keeping a clear approval point between a suggestion and an edit. ## Sessions, Permissions, and Diffs A session is one conversation with Claude about one body of work. It keeps the messages, selected project, and proposed changes together. Starting a fresh session for a new job helps prevent old instructions from bleeding into unrelated work. Permissions control which actions Claude can take without stopping. Manual asks before file edits and commands. Accept edits can apply file changes more freely, while Plan asks Claude to describe an approach without editing anything. | Mode | What Claude Does | Beginner Use | | Manual | Waits for approval | Start here | | Plan | Explains before editing | Use for bigger work | | Accept edits | Applies file edits | Use after practice | Read the request, read the file list, read the diff, and run the relevant check. Claude's summary is helpful, but the diff is the evidence of what changed. A successful test proves only that the tested behaviour passed. ## Local, Cloud, or SSH Local keeps the session beside the files and tools already installed on your computer. Choose it when the project needs your local database, private development settings, or software that exists only on that machine. Closing the app stops the local work. Cloud sends the task to an Anthropic-managed environment. The desktop quickstart says these sessions continue even after the app closes. Cloud fits a longer job you want to check later, although you still need to review the returned changes before treating them as finished. SSH stands for Secure Shell, a way to connect to another computer over a network. Claude Desktop can use SSH for a remote server or development machine. Leave this option alone until you already understand which remote machine you are opening and what the session can change there. | Environment | Where It Runs | Choose It When | | Local | Your computer | You need local files and tools | | Cloud | Anthropic infrastructure | The task should keep running | | SSH | Another computer | You manage a remote machine | ## The CLI Path for Terminal Users The terminal route remains useful for people who already work there. CLI means command-line interface, a program controlled by typed commands. Anthropic's [setup documentation](https://code.claude.com/docs/en/setup) now points graphical-interface users to Desktop before listing the CLI installers. The official CLI options include a curl installer on macOS and Linux, a Homebrew cask on macOS, and WinGet on Windows. Node.js is no longer a prerequisite for the recommended native install. Follow the operating-system instructions on Anthropic's live setup page because installer details can change. - `curl -fsSL https://claude.ai/install.sh | bash` is the native macOS and Linux installer. - `brew install --cask claude-code` installs the stable Homebrew cask on macOS. - `winget install Anthropic.ClaudeCode` installs the Windows package with WinGet. After installation, open a terminal in an existing project folder and run `claude`. That opens an interactive session using the repository in that folder. The CLI is a secondary starting path here because a command typed in the wrong folder can point the agent at the wrong files. Terminal users can move an active CLI session into the app with `/desktop`. Anthropic says the command saves the session, opens it in Claude Desktop, and exits the CLI on supported subscription accounts. This is handy when a task begins in text and grows into a diff you would rather review visually. ## What Claude Code Does Well Claude Code is strongest when one outcome touches several related files. A login change may involve a page, a validation rule, a test, and a type definition. The agent can trace those connections and propose a coordinated set of edits instead of treating every file as a separate conversation. It can also run the checks already defined by the project. A test is a repeatable check that confirms one piece of behaviour. A build turns the project into the form it needs for release. Claude can run both, read failures, and revise its work. > The command output and the diff provide useful proof; the agent's confidence is only a claim. Claude Code is also good at investigation work. You can ask where a value comes from, which file owns a button, or why a test fails. Beginning with a read-only explanation is often the best way to learn an unfamiliar repository before asking for a change. Longer tasks work best when the outcome and boundaries are visible. Name the broken behaviour, the result you expect, the check that should pass, and anything Claude must leave alone. That gives the agent a finish line without handing it permission to redesign the surrounding project. ## Where Claude Code Needs Supervision Claude Code can misunderstand a vague request and make a polished change to the wrong part of a project. It may also find an existing pattern that should not be copied. Give it a small first job, inspect the changed file list, and stop the session when the work moves outside the stated boundary. A large diff takes time to review even when Claude produced it quickly. Desktop makes the review easier to see, but it does not make a broad change safer by itself. Ask for separate, reviewable steps when one task mixes a database change, user-interface work, and deployment configuration. Claude Code also lacks Cursor's inline Tab completions. It will not quietly suggest the next few lines while you type in Desktop. The Code tab works through explicit conversations and proposed edits, which suits delegation better than moment-to-moment typing help. Paid plans have usage limits, and activity across Claude's web, desktop, mobile, and Claude Code surfaces draws from the same plan pool. A long coding session can therefore affect how much Claude you have available elsewhere. Check the usage display before starting work that cannot pause. ## How Much Does Claude Code Cost? For individuals, Claude Code is included in Pro at $20 monthly or $200 billed annually, and Max starts at $100 per month. Team and Enterprise plans also include access. Choose the allowance after trying real tasks, then select the model that fits them. Claude Code lets you choose among model families. The model is the AI doing the reasoning, reading, and writing. Anthropic's [model configuration page](https://code.claude.com/docs/en/model-config), describes model families for different workloads. Specific versions and defaults vary by provider and account, so check the model selected in your session before comparing cost or performance. | Model Family | Plain-Language Fit | | Haiku | Fast, simple jobs | | Sonnet | Daily coding | | Opus | Complex reasoning | | Fable | Longest, hardest tasks | Availability can depend on the plan, organization settings, and provider. The beginner path is to keep the selected default and learn the workflow first. Move to another family when the current model is too slow, too costly for the allowance, or unable to reason through a difficult task. The [Claude pricing page](https://claude.com/pricing), lists Pro at $17 per month with an annual subscription, with $200 billed up front, or $20 month to month. Pro includes Claude Code, Cowork, and Design. Current Max pricing starts at $100 per month. It lets you choose 5x or 20x more usage than Pro, and Anthropic lists higher output limits for all tasks. Both Max options are billed monthly, so the annual Pro discount does not describe the Max purchase. Max 5x and Max 20x now include $100 and $200 a month in [API credits](https://platform.claude.com/docs/en/about-claude/api-credits-for-subscribers), and Team includes $20 per Standard seat or $100 per Premium seat. The credits cover the Claude API and Agent SDK, not Claude Code usage, and expire at the end of each billing cycle. | Plan | Verified Price | What Changes | | Pro annual | $17/mo, $200 up front | Claude Code included | | Pro monthly | $20/mo | Claude Code included | | Max | From $100/mo | 5x or 20x Pro usage, plus API credits | My take: Pro is the sensible first paid plan for learning the product. Move to Max after real sessions show that the Pro limits interrupt useful work. Paying for theoretical capacity before you know your own pattern is an expensive way to avoid one small experiment. ## Code Review Code review means inspecting a proposed change before it joins the main version of a project. Claude Desktop can show the current diff and offers a Review code action. Anthropic says that review focuses on issues such as definite logic errors, compile errors, security problems, and obvious bugs. Claude Code also connects to GitHub, the service many teams use to store repositories and discuss proposed changes. A pull request, usually shortened to PR, is GitHub's review page for a set of changes. Claude can help create a PR, monitor checks, and surface failures from the desktop session. Experienced teams also use Claude Code through local review commands and managed GitHub review tools. Those routes have their own setup and cost questions, so the [AI code review hub](/ai-code-review/) covers them in more detail. A beginner can start with the desktop diff and a second read before accepting. ## The Claude Code Ecosystem Claude Code now follows a task across several surfaces. The terminal remains useful for scripting and automation. Desktop adds visual review and session management. The web runs work in an Anthropic-managed environment, while mobile access and Dispatch let you send or monitor tasks away from the computer. | Surface | What It Adds | Best First Use | | Desktop | Visual diffs and sessions | Beginner local task | | CLI | Terminal workflow | Existing terminal users | | Web | Cloud task handoff | GitHub-connected work | | Mobile and Dispatch | Remote task access | Check long-running work | | GitHub | PR and check handoff | Review before merging | The first session does not need every available surface. Desktop is enough for the first local session, and its Cloud option covers the first background task. Add the terminal, GitHub, or Dispatch route when your existing work already has a reason to move there. ## How Claude Code Got Here Anthropic introduced Claude Code on February 24, 2025, alongside Claude 3.7 Sonnet. The [launch announcement](https://www.anthropic.com/news/claude-3-7-sonnet) called it a limited research preview and described it as a command-line tool for delegating engineering work from a terminal. A terminal is the text-only window where a user types commands instead of clicking through menus. Claude Code's first home made sense for experienced developers because their tests and tools for tracking changes and publishing software already lived there. It also gave the product a reputation for being a power tool with a steep entrance. The original idea was bigger than code completion. Earlier AI assistants mainly predicted the next line while someone typed. Claude Code could inspect several files, decide on a sequence of actions, make coordinated edits, run a test, read the failure, and try again. Anthropic then carried that engine into a wider set of surfaces. Claude Code now has the original command-line interface (CLI), the Code tab in Claude Desktop, a browser version, editor integrations, mobile access, Dispatch for sending work from a phone, and connections to GitHub workflows. The [platform guide](https://code.claude.com/docs/en/platforms) lists the current routes into the same product. That growth explains why people talk about Claude Code as more than a chatbot. You can begin a local task beside your files, send a longer job to the cloud, check it from another device, and review proposed changes on GitHub (the website where most teams store and share their code) before they join the main project. Developers call that last review step a pull request. The product moved from one terminal window into an ecosystem for handing work between places. ## Compare Claude Code The [AI coding tools comparisons](/ai-coding-tools/comparisons/) separate Claude Code's task-first workflow from the tools around it: - [Cursor vs Claude Code](/ai-coding-tools/comparisons/cursor-vs-claude-code/) compares editor-first work with task delegation. - [Claude Code vs GitHub Copilot](/ai-coding-tools/comparisons/claude-code-vs-copilot/) compares an agent with inline editor help. - [Claude Code vs OpenAI Codex](/ai-coding-tools/comparisons/claude-code-vs-codex/) compares two delegated-agent workflows. - [Claude Code vs OpenCode](/ai-coding-tools/comparisons/claude-code-vs-opencode/) compares commercial and open-source terminal agents. ## The Verdict Claude Code is the strongest first choice when you want to hand an AI a complete coding task and keep a clear review step before its changes become yours. Start in Claude Desktop, use Local and Manual mode, choose one project you understand, and accept only the diff you can explain. Pro is enough for the first serious trial. Move to Max after the usage limits interrupt work you have already proved valuable. Add the CLI when the terminal is already part of your routine, because the graphical route now teaches the same core product with far less setup friction. ## FAQ ### Do I need the terminal to use Claude Code? Claude Code does not require terminal commands when you use the Code tab in the Claude Desktop app. The desktop interface lets you select a project folder, describe a coding task, and review proposed file changes visually. ### How much does Claude Code cost? Claude Code access starts with Claude Pro at $20 billed monthly or $200 billed annually. Claude Max starts at $100 per month and offers a choice of 5x or 20x more usage than Pro, plus higher output limits. Team and Enterprise plans also include Claude Code. ### Is Claude Code included in Claude Pro? Claude Pro includes Claude Code, Cowork, and Design. The same subscription works across Claude's web, desktop, and mobile apps, while your plan usage is shared across those surfaces. ### What is a diff? A diff is a before-and-after view of changed text. Claude Desktop shows each proposed file change and gives you Accept and Reject controls in Manual mode, so you can decide what reaches your files. ### Can Claude Code work while the app is closed? Claude Code can keep working after you close the desktop app when you choose a Cloud session. Anthropic says cloud sessions continue on its infrastructure after you close the desktop app, and you can return later to review or redirect the work. ### Which model should a beginner pick? Beginners using Claude Code should start with the model already selected in the desktop app. A model is the AI system doing the reading and writing, and the default is chosen for your account. Change it only when you have a clear reason, such as moving a difficult task from the Sonnet family to the Opus family. ### Claude Code vs Cursor: which should you use? Developers should choose Claude Code Desktop when they want to describe a whole coding task and review the finished changes in one place. Cursor suits developers who want suggestions while typing inside a VS Code-style editor. I use both: Cursor for editing in place and Claude Code as the control room for larger agent tasks. ## Going Deeper This main page stops after the basic orientation. Once you have completed several small sessions, the [Configuration and Context guide](/ai-coding-tools/claude-code/configuration-and-context/) explains project instructions, the [MCP and Hooks guide](/ai-coding-tools/claude-code/mcp-and-hooks/) covers outside-tool connections, and the [Workflows and Safety guide](/ai-coding-tools/claude-code/workflows-and-safety/) covers stricter operating boundaries. Beginners who want an even slower walkthrough can use the [Claude Desktop guide](/ai-coding-tools/claude-code/claude-desktop/). It stays with the graphical route and explains the account, project, first request, and next steps without assuming the terminal is familiar. Desktop support, permission guidance, and plan details reviewed against official documentation on October 17, 2026. --- ## Complete Guide to OpenAI Codex (2026) URL: /ai-coding-tools/codex/ Cluster: AI Coding Tools > Overview & AI Coding Tools Codex is OpenAI's AI coding agent. It can inspect a repository, edit files, run checks, and return a diff for review through desktop, terminal, editor, web, and cloud workflows. That sounds simple until several tasks finish at once and the bottleneck moves from writing code to judging it. I use Codex beside Claude Code as a second agent and independent reviewer. Its value in that role depends on a clear task and enough attention to check the result. Our [best AI coding tools guide](/ai-coding-tools/) places Codex alongside Cursor, Claude Code, and the other tools we cover. ## What Is OpenAI Codex? Codex is OpenAI's coding agent across several surfaces: the ChatGPT desktop app, CLI, web, IDE access, cloud tasks, code review, and mobile access. On July 9, 2026, OpenAI folded the standalone Codex desktop app into ChatGPT desktop. That does not turn Codex into ordinary chat. It keeps a dedicated coding experience beside Chat and Work, while the terminal and cloud options remain available for the jobs that suit them. That distinction matters when you decide which tasks to hand off. Codex is useful when a task can be stated with enough context to run independently, then returned as output you inspect. It is less useful when the job is still a conversation with yourself. Start with a focused task, name the relevant files and expected behaviour, and review the changes and tests before you merge. ## Where Codex Fits Codex fits work you can describe, check, and review: a known bug, a test gap, a bounded refactor, or a second opinion on a diff. Keep unsettled product decisions in an interactive conversation until the requirements are clear. It works well beside [Cursor](/ai-coding-tools/cursor/) in my workflow. Use the surface that makes the next decision easiest to inspect, and keep enough time to review every delegated task. ## Choosing a Current Codex Model Start with GPT-6.1 Sol for complex coding when your account offers it, Luna for focused repeatable tasks, or Astra for the most demanding work. OpenAI's [model guide](https://learn.chatgpt.com/docs/models) describes these choices; the model picker shows what your plan and workspace permit. | Model | Useful Starting Point | Access Check | | GPT-6.1 Sol | Complex coding | Eligible paid plans and clients | | GPT-6 Luna | Focused repeatable tasks | Free/Go desktop and paid plans | | GPT-6 Astra | Most demanding tasks | Plan and workspace eligibility | GPT-6.1 Sol's launch includes Plus, Pro, Business, Enterprise, and Edu, with administrator enablement required for Enterprise and Edu. Earlier models may remain available. A stronger model does not change the task's permissions or remove the need to review its output. ## Where You Use Codex Use ChatGPT desktop when you want the dedicated coding workspace alongside your Chat and Work context. Use the CLI when you are already in the repository and want an interactive terminal session. Use cloud tasks when a bounded job can run away from your machine, and use the mobile surface to steer or check an active task when you are away from the desk. The [Codex desktop guide](/ai-coding-tools/codex/codex-desktop/) covers the app workflow; the [Codex mobile guide](/ai-coding-tools/codex/codex-mobile/) covers what a phone can responsibly do. Keep the work surface matched to the task. A quick codebase question may belong in the CLI, a long-running refactor may suit a cloud task, and a full review still deserves a real screen. Productivity comes from handing off work you can specify, then spending your attention on the result. ## Writing Tasks Codex Can Finish The task description does most of the steering. "Fix the login bug" leaves Codex to guess which screen is broken, what the right behaviour is, and how you will test the result. A better handoff names the symptom, the likely files, the boundary, and the proof: show an error when the email field is empty, keep validation in the existing helper, and run the login-form test. Codex can still inspect the repository, but it starts with a finish line. That format works for bug fixes, test gaps, repeated edits, dependency-free refactors, and research inside a codebase. It works less well for a feature whose product rules are still unsettled. If you cannot tell a teammate what "done" means, the agent will fill the gap with assumptions. Write the decision first, or keep the session interactive until the open questions are settled. State the forbidden moves as plainly as the desired result. Tell Codex not to add a package, edit generated files, touch a migration, or expand the task beyond the named route. Name the command that proves the work and any check it cannot run in its sandbox. These limits make review faster because an unexpected file or skipped command is visible at once. Cloud tasks are most useful when the work can run without your machine. Give each task its own narrow outcome and let Codex return a diff while you do something else. Running three agents does not make one vague request better; it gives you three vague results to review. Parallel work pays off when the jobs are separate, such as one test repair, one small refactor, and one codebase question that produces a written answer. For a first task, pick work you understand well and could finish yourself in an hour. Compare the returned diff with the route you expected, note where Codex needed more context, and use that finding in the next handoff. This is a safer way to learn the agent than starting with auth, a database migration, or a release job where a plausible mistake carries a real cost. ## Terminal, PR Review, and CI Codex CLI brings the agent to your terminal, where it can work with the project you have open and ask for approvals according to the mode you choose. That is the right surface for a live investigation, a narrow fix, or a local review before you push. The repository's test suite, branch protections, and human release judgment remain the shipping gates. For pull requests, Codex can review GitHub changes and return comments for you to assess. Give it project instructions that name real risks, then treat the review as another input rather than a merge approval. OpenAI also documents Codex workflows with GitHub Actions, which makes CI-style automation possible for bounded jobs. Keep credentials, network access, and write permissions as narrow as the job allows. ## Reviewing Delegated Work When a task finishes, start with the file list before reading the summary. A request for one form should not quietly rewrite shared auth code, add a package, or rename a public API. Then read the diff, run the named checks, and inspect any test that Codex added. Generated tests can pass while proving the wrong behaviour, especially when the agent wrote both the code and its proof from the same mistaken assumption. The live task log can warn you before the final diff arrives. If Codex spends time in unrelated folders or retries the same failing command, the task may be drifting. Step in when the route is plainly wrong or the work is stuck in a loop. If the agent is making steady progress inside the stated boundary, let it finish and respond to the result; constant mid-task steering can create a second set of assumptions. Review effort does not disappear when agents run in parallel. Three completed tasks mean three diffs, three test results, and three chances for work to overlap. Staggering the handoff can be better than launching everything at once, because the first result may alter the base or expose a rule the later tasks need. Codex saves writing time only when you keep enough attention for that queue. Pull-request review follows the same rule as delegated coding work. Treat a Codex comment as a claim to test. Reproduce the bug, trace the code around the diff, and ignore advice that does not fit the product or repository. Project instructions such as `AGENTS.md` can tell the reviewer which risks matter here, but they do not replace the person who knows why the feature exists. A finished task can be reviewed in four passes: 1. Compare the changed-file list with the task boundary before reading the summary. 2. Read the diff for altered behaviour, missing error paths, and edits unrelated to the request. 3. Run the named checks yourself and inspect whether any generated test proves the intended result. 4. Verify the feature in the surface where a user or operator will encounter it. Keep the agent's task log as supporting evidence. It can show which commands ran, what failed, and where the model changed direction. It cannot prove the final state of your repository after another task lands. Run checks against the exact diff you intend to merge, especially when several agents worked from nearby commits. ## Where Codex Does Not Fit Skip cloud-connected Codex when policy requires entirely offline processing, when policy rules out a cloud-connected agent, or when the work is mostly product decisions that nobody has made yet. It also loses its advantage when you cannot state what success looks like. A vague task can return a polished-looking diff that solves the wrong problem. In my workflow, Codex runs beside [Claude Code](/ai-coding-tools/claude-code/) as a second agent and independent reviewer, while Cursor cloud agents help verify auto-deployed development environments. Choose the surface that exposes enough context for the next decision. ## Codex Verdict Codex is the best fit when a coding job can be bounded, delegated, checked, and returned as a diff or pull request. Its CLI supports close local work, while cloud tasks and review let separate jobs continue away from the editor. It saves you from writing every change yourself, while leaving you responsible for checking the result. Keep the Codex session interactive when the task is still being discovered, or use the conversational tool you prefer. Choose an AI-first editor when visual file work dominates. Skip cloud-connected Codex work when policy requires the code to stay on controlled infrastructure. ## FAQ ### Is Codex included in ChatGPT plans? Codex access is included in ChatGPT plans, with availability and usage limits determined by the plan. Free and Go have limited desktop access; paid plans expand access and usage. API-key access is billed separately and does not include Codex cloud. Check current pricing and workspace settings before choosing a plan. ### Which models can Codex use? Codex model options include GPT-6.1 Sol, GPT-6 Luna, and GPT-6 Astra, with earlier models available during transitions. Your model picker shows which models your plan, client, and workspace allow. ### Codex vs Claude Code, when does each fit? Choose Codex when its ChatGPT integration, delegated tasks, and review workflow fit your work; choose Claude Code when you prefer a Claude-focused investigation and editing workflow. Claude Code also offers desktop, terminal, editor, and cloud workflows. Compare permissions, account route, review handoff, and the same bounded task before treating either product description as a decision. ### Does Codex still exist as a separate app? Codex still exists as a dedicated coding experience, but the standalone Codex desktop app was folded into the ChatGPT desktop app on July 9, 2026. In that app, Codex remains its own view alongside Chat and Work. The Codex CLI, web and supported mobile surfaces remain separate ways to reach the same product. ### Can Codex run in my terminal or CI? Yes, Codex CLI runs in your terminal with configurable approvals for local coding work. OpenAI also publishes Codex workflow examples that use GitHub Actions, so teams can incorporate it into CI-style automation. Keep permissions narrow, treat generated changes as reviewable output, and make repository tests the final gate. ## Compare OpenAI Codex - [Cursor vs Codex](/ai-coding-tools/comparisons/cursor-vs-codex/) compares the editor-first and delegated-agent workflows. - [Claude Code vs OpenAI Codex](/ai-coding-tools/comparisons/claude-code-vs-codex/) compares the two terminal and agent workflows directly. - [ChatGPT desktop Codex](/ai-coding-tools/codex/codex-desktop/) covers the visual task workspace. - [Codex mobile](/ai-coding-tools/codex/codex-mobile/) covers responsible mobile task steering. Use those guides to separate client setup from the review process. The same task still needs a clear boundary, approval mode, and repository check. --- ## AI Coding Tool Comparisons URL: /ai-coding-tools/comparisons/ Cluster: AI Coding Tools > Overview & AI Coding Tools Use these AI coding tool comparisons when you have two tools in mind and want to understand the tradeoffs before choosing. Each head-to-head guide compares the workflows, costs, compatibility, and control that matter for that particular decision. If you have not formed a shortlist yet, start with our [best AI coding tools guide](/ai-coding-tools/) for my overall recommendations. Return here when you want to compare a specific alternative. My recommendations, and those of contributors to the comparison guides, reflect our own experience; your editor requirements, team policies, and budget may lead you to a different choice. ## Which AI Coding Tools Should You Compare? Compare the tool you already use with the alternative that addresses your main limitation. For an editor switch, start with Cursor vs VS Code; for a choice between coding agents, start with the relevant Claude Code, Codex, or OpenCode comparison. | Your decision | Start here | | Replace VS Code or keep your editor? | [Cursor vs VS Code](/ai-coding-tools/comparisons/cursor-vs-vscode/) | | Choose Cursor or add Copilot to your workflow? | [Cursor vs GitHub Copilot](/ai-coding-tools/comparisons/cursor-vs-copilot/) | | Compare Cursor with Claude Code? | [Cursor vs Claude Code](/ai-coding-tools/comparisons/cursor-vs-claude-code/) | | Choose between Anthropic and OpenAI agents? | [Claude Code vs Codex](/ai-coding-tools/comparisons/claude-code-vs-codex/) | | Consider an open-source agent instead? | [Cursor vs OpenCode](/ai-coding-tools/comparisons/cursor-vs-opencode/) or [Claude Code vs OpenCode](/ai-coding-tools/comparisons/claude-code-vs-opencode/) | The table gives common starting points. The groups below include every published comparison in this section, including the remaining tool pairings and broader workflow or model decisions. ## Browse All Comparisons ### Cursor Matchups ### Claude Code Matchups ### Workflow and Model Comparisons ## How Should You Evaluate Two AI Coding Tools? Evaluate two AI coding tools against the same task, required integrations, and total cost of completing work you can review. A longer feature list is less useful than knowing whether a tool fits your repository and makes the finished change easier to maintain. - Workflow: Decide whether you need inline suggestions, interactive agent work, or tasks you delegate and review later. Many tools support several of these patterns, so avoid choosing solely from an editor or terminal label. - Compatibility: Check the extensions, language tools, remote connections, and organizational policies you rely on. An attractive agent workflow cannot compensate for a required integration that does not work. - Total cost: Compare the subscription, included usage, separately billed models, and time spent correcting or reviewing results. Verify current prices and limits in the linked vendor documentation before buying. - Control: Check where code is processed, which commands need approval, and whether the tool supports your required model providers or local setup. Match those controls to the work you intend to delegate. For a practical comparison, give each tool a small task from the same starting repository state. Keep the acceptance criteria and permissions consistent, and record unrelated edits, failed checks, correction time, and usage charges. Choose the result that fits your work rather than assuming my preferred tool must be yours. ## What If You Are Comparing More Than Two Tools? Use the [AI coding tools overview](/ai-coding-tools/) to narrow a larger shortlist, then read the head-to-head guides for your final candidates. If your main uncertainty is how you want to work, the workflow comparisons above can help you decide before choosing a product. For example, readers weighing Claude Code, Codex, and OpenCode can compare Claude Code with Codex first, then use Claude Code vs OpenCode to examine the provider-choice and open-source tradeoffs. Those guides answer different parts of the same decision without requiring you to buy all three tools. If you are new to AI-assisted development, our [beginner's guide to AI coding tools](/ai-coding-tools/guides/getting-started/) explains a first coding session. If your main constraint is spending, the [free AI coding tools guide](/ai-coding-tools/guides/free-tools/) is a more useful starting point than comparing paid plans you do not need. --- ## Complete Guide to GitHub Copilot (2026) URL: /ai-coding-tools/copilot/ Cluster: AI Coding Tools > Overview & AI Coding Tools GitHub Copilot is an AI coding assistant for code completion, chat, multi-file edits, and pull-request work across supported editors and GitHub. It suits teams that want AI assistance within their existing tools and centrally managed GitHub workflow. Nobody on our team uses Copilot daily anymore, and we have not run a fresh hands-on test. It still offers broad editor support, organization controls, and a familiar review path. Whether that is enough depends on how you work. Our [AI coding tools ranking](/ai-coding-tools/) compares Copilot with the alternatives and explains why a familiar editor extension is not our only selection criterion. ## Who Is GitHub Copilot For? GitHub Copilot suits teams that already use GitHub issues and pull requests and want to keep their current editors. Its value is the connection between those tools, not a requirement to move everyone into one IDE. If the main need is a dedicated workspace for supervising agents, compare that workflow directly with Cursor or Codex. Copilot also offers agent workflows; editor support alone does not decide which product will save you review time. ## What Can GitHub Copilot Do in Your Editor? GitHub Copilot suggests code as you type and provides chat for explaining code, investigating failures, and drafting tests in supported editors. Its inline suggestions appear as ghost text that you can accept or ignore without switching context. It is useful for repetitive tests, imports, configuration forms, and code that follows a nearby pattern. Keep the final judgment with the developer when the work involves product rules, security, or an edit that crosses several systems. Copilot Chat adds a conversation to that same editor. Select the relevant code before asking a question, then use the answer to inspect a failure, explain unfamiliar code, draft tests, or plan a small edit. The code, Git history, pull request, issue, and assistant sit in the tools a GitHub team already knows. Copilot's reach also matters for teams that use several editors. GitHub documents Copilot in VS Code, Visual Studio, JetBrains IDEs, Xcode, Eclipse, Neovim, the command line, GitHub.com, and GitHub Mobile. That makes Copilot easier to introduce to a mixed-editor team than a tool that requires one editor choice. Give Copilot relevant code and a concrete task: a nearby test or type helps completion, while a selected function gives a chat question useful context. Read suggestions before accepting them; fluent code can still repeat a bad assumption. ## How Do Copilot’s Agent Mode and Cloud Agent Differ? GitHub Copilot’s Agent mode works in your supported IDE, where it can edit multiple files and run approved commands. Copilot’s cloud agent runs a task in an isolated GitHub Actions environment and returns a pull request. In both cases, give it an outcome, file boundary, and named check; then read the diff before accepting its explanation. The [Copilot setup and enterprise guide](/ai-coding-tools/copilot/setup-and-enterprise/) covers repository instructions, cloud-agent setup, and organization controls. Treat review comments as claims to investigate, not merge approval. ## How Much Does GitHub Copilot Cost? Copilot has a free tier. Individual paid plans cost $10, $39, or $100 per month; Business and Enterprise cost $19 and $39 per seat per month. AI-credit allowances and additional usage affect the total bill. | Plan | Price | Best for | | Free | $0 | A first trial run | | Pro | $10/month | Solo daily use | | Pro+ | $39/month | Premium models, more credits | | Max | $100/month | Heavy agent work | | Business | $19/seat/month | Team policy controls | | Enterprise | $39/seat/month | Enterprise governance | GitHub's [plans page](https://docs.github.com/en/copilot/get-started/plans) lists the current plan prices and included capabilities. Compare equivalent capabilities, not just the cheapest seat. Cursor's current Plus plan is $8 per month, while Premium is $30; older Pro pricing belongs to legacy plans. The seat price is only the first number. GitHub lists 1,500 total monthly AI credits for Pro, 7,000 for Pro+, and 20,000 for Max, including each plan's base and flex allotments. Chat, Agent Mode, code review, the cloud agent, the CLI, and Copilot Apps draw from those credits. Unlimited completions do not mean unlimited agent work, so check the usage dashboard during a trial instead of estimating from prompt counts. ## How Do You Give Copilot Repository Instructions? You can give GitHub Copilot repository-wide coding instructions in `.github/copilot-instructions.md` at the repository root. Use the file to describe your stack, naming conventions, folder boundaries, and test commands; support depends on the Copilot feature and client. Keep instructions short and tied to mistakes you have actually seen. A specific convention helps more than a broad style essay. Instructions guide generated code; they do not enforce security boundaries or prove an edit is correct. Review the diff and run the relevant checks before merging. The [Copilot setup and enterprise guide](/ai-coding-tools/copilot/setup-and-enterprise/) covers configuration and organization controls in detail. ## Which Models Does GitHub Copilot Support? GitHub Copilot supports models from several providers, including OpenAI GPT, Anthropic Claude, Google Gemini, and xAI Grok. The available selection depends on your plan, client, and organization policy; Copilot Free uses automatic model selection. Choose a model for the task’s difficulty, response speed, and credit cost rather than its release date. GitHub’s [supported-model documentation](https://docs.github.com/en/copilot/reference/ai-models/supported-models) lists current availability. Check your model picker before planning a workflow around a particular model. ## Teams, Privacy, and Governance Business and Enterprise are where Copilot becomes an organization product. GitHub says both plans provide centralized management, policy control, a broad model catalog, cloud-agent access, and pooled AI credits. Enterprise adds a larger credit pool and enterprise-grade capabilities for GitHub Enterprise Cloud customers. Check organization policy and the exact surface before pointing an agent at sensitive repositories. Content exclusions do not apply to IDE Edit or Agent modes, Copilot CLI, or the cloud agent; indirect IDE context can also expose information from excluded files. Those controls should reflect the code your team is allowed to send to Copilot, rather than assuming every folder is safe because the repository is private. The detailed instructions, CLI workflow, and enterprise setup checklist are in the [Copilot setup and enterprise guide](/ai-coding-tools/copilot/setup-and-enterprise/). ## Limits and Fit Copilot is a good default for an existing GitHub team when keeping editor choice open and administering the review path in GitHub matter more than moving to another coding workspace. That fit has practical limits for agent-heavy work. A team that wants an AI-first editor organized around several local agents should test that workflow directly before standardizing on Copilot. Acceptance rates need a published method and a comparable task set before they support a buying decision. Run Copilot on a real bug, a small feature, and a pull-request review in your own repository. Check the diffs, test results, credit use, and any policy friction before you buy seats for a whole team. Keep the repository, model, feature, and account policy fixed for each comparison run. That controls the variables that a price table cannot show. ## Copilot Verdict GitHub Copilot has dropped down our rankings, and nobody on our team uses it daily any more. Its remaining case is real but narrow: a team that already lives in GitHub, wants completions, agent work, and pull-request review administered centrally, and will not change editors. For everyone else, [Cursor](/ai-coding-tools/cursor/) is our recommendation, and AI-credit use can change Copilot's monthly cost once agents and premium models become routine in any case. Choose Cursor when the editor itself should be organized around visual, parallel agent work. Choose a terminal-first agent when repository investigation and command-heavy tasks matter more than editor reach. Whichever route you take, test one real issue through to a reviewed diff before buying seats. ## FAQ ### Is Copilot cheaper than Cursor? Copilot is cheaper than some Cursor plans, but not all: Copilot Pro costs $10 per month, compared with $8 for Cursor Plus and $30 for Cursor Premium. Copilot Business is $19 per seat, while Cursor team tiers start at $30. Compare model access, agent features, and extra usage before choosing on price. ### What's in Copilot Free? Copilot Free includes 2,000 code completions per month, limited agent access, and an allowance of AI credits for individual developers. It uses automatic model selection rather than a manual model picker. GitHub’s plans page does not specify a numerical Free-plan AI-credit allowance, so check your account for the available balance. ### Is Copilot worth it for teams? Copilot can be worth paying for when a team needs GitHub-centered administration, shared usage controls, and agent workflows in its existing editors. The decision depends on whether those GitHub controls match the repositories and editors in scope, so test a bounded pull-request workflow before rolling out seats. ### Does Copilot have an agent mode? GitHub Copilot’s Agent mode can work across files and run commands with your approval in supported IDEs. Copilot also has a cloud agent that works in an isolated GitHub Actions runner, where it can take a well-defined task and open a pull request for human review. Review the plan and resulting diff before merging either kind of work. ### Which models does Copilot use in 2026? GitHub Copilot supports OpenAI GPT, Anthropic Claude, Google Gemini, and xAI Grok models, among others. The models available to you depend on your plan, client, and organization policy. Copilot Free uses automatic model selection; check the model picker and GitHub’s supported-model list for your current options. - Read the [Copilot setup and enterprise guide](/ai-coding-tools/copilot/setup-and-enterprise/) before rolling it out to a repository or organization. - Compare the editor workflow in [Cursor vs Copilot](/ai-coding-tools/comparisons/cursor-vs-copilot/). - See how Copilot compares with a terminal agent in [Claude Code vs Copilot](/ai-coding-tools/comparisons/claude-code-vs-copilot/). Editorial update (October 17, 2026): Reorganized the guide and checked GitHub’s plan allowances and model documentation. This update does not represent a new hands-on test. --- ## Complete Guide to Cursor (2026) URL: /ai-coding-tools/cursor/ Cluster: AI Coding Tools > Overview & AI Coding Tools Cursor is an AI code editor built from VS Code's open-source foundation. It combines code completion, project questions, file editing, and agents that can run development commands. You can inspect the files and changes in the same workspace where you write code. We rank Cursor second, behind Claude Code, in our [AI coding tools comparison](/ai-coding-tools/), with Grok Bot adding a separate option for delegated work. This guide explains the editor, its limits, and a first task you can check yourself. Your choice still depends on the extensions, model access, and billing your project needs. | Your Requirement | Cursor's Fit | | Visual editing and review | Files, AI chat, and diffs together | | Existing VS Code workflow | Familiar layout; verify required extensions | | Delegated background work | Cloud agents and eligible Grok Bot access | | Hosting and database included | Separate project services still needed | ## What Is Cursor? Cursor is a standalone code editor with integrated AI assistance, so it can replace VS Code for many workflows. It opens a repository, often shortened to repo, which is a project folder containing code, supporting files, and the recorded history of changes. The editor keeps ordinary programming tools around the AI. You can browse files, search text, open a terminal, review the project's recorded changes, install extensions, and pause a running program to inspect an error. The AI tools can see the code surrounding your task and can propose edits inside the same window. A terminal is a text-based window for typing commands. Cursor includes one because many projects use commands to start the app, run tests, or create a release. Agent Mode can run those commands for you. A beginner should keep the setting that asks for approval before each command until those commands are familiar. Cursor also offers access to several AI model families. A model is the underlying AI system that reads the request and produces the response. Cursor provides access to its own models and models from other companies, while the plan and current account determine which choices appear. ![image]()Cursor's agent-first home puts New Agent and Automations beside the workspace list. ## What Can Cursor Do? Cursor can suggest code, explain selected code, edit project files, run development commands, and delegate tasks to cloud agents. Tab helps with the next edit you are making. Chat and inline edit help with one question or selected area. Agent coordinates work across files and can run commands, while cloud agents run a handed-off task on remote infrastructure. Composer is also the name of Cursor's coding-model family; a model supplies reasoning within the agent workflow. | Tool | Job Size | What You Review | | Tab | Next edit | Suggested text | | Inline edit | Selected area | Focused diff | | Chat | Question or explanation | Answer and cited files | | Agent | Multi-step task | Files and command output | | Cloud agent | Remote handoff | Result and artifacts | ### Tab Completion Tab completion predicts an edit while you type. It may finish a line, add several related lines, or suggest the same rename in the next likely place. Press Tab to accept the suggestion and Escape to dismiss it. This is Cursor's lightest form of help because you remain in control of the file and see the suggestion where it will land. It is useful for repetitive work and familiar patterns. Read the whole proposed block when the suggestion crosses several lines. ### Chat and Inline Edit Chat answers questions about the project and can use the selected code as context. Context means the information the model can see while answering, such as the current file, selected lines, or other files you explicitly include. Inline edit changes a selected section of code. On macOS, `Cmd+K` opens that flow; Windows uses `Ctrl+K`. Select the smallest area that owns the change, state the outcome, and inspect the focused diff. ### Agent Cursor Agent searches the project, edits files, and can run commands, then uses the results to continue the task. [Composer 2.5](https://cursor.com/docs/models/cursor-composer-2-5) is a coding model that can power this workflow. Older tutorials also use Composer for a multi-file editing interface, so check which meaning a tutorial intends. The name agent describes software that can take a sequence of actions toward a goal instead of returning a single answer. Start in Ask or Plan when you need an explanation first. Move to Agent only after the task, project area, and expected check are clear. Keep command approval enabled while you are learning what the project runs and which commands can affect data. ![image]()The mode menu separates planning and questions from modes that can change the project. ### Cloud Agents A cloud agent runs on a remote computer managed by Cursor. It receives a repository and a task, prepares an environment, works through the request, and returns a result for review. Your laptop does not need to carry the whole job. Cursor's [cloud-agent engineering notes](https://cursor.com/blog/cloud-agent-lessons) explain that remote agents create extra work around environment setup, reliability, and orchestration. In plain language, the cloud computer must be taught how to install and run your project before the agent can produce a trustworthy result. Cloud agents suit longer, clearly bounded work that can proceed without constant questions. They are a poor first choice for a project that only runs on your laptop or a task whose expected result is still unclear. Begin locally, then move a repeatable job to the cloud. [Cursor Projects](https://cursor.com/blog/projects) adds a coordinator for work that spans several agent tasks or pull requests, such as a migration or an ongoing maintenance job. It is rolling out in beta, so check availability in your account before planning around it. ## Getting Started Start with one project you already understand. A small personal site, a class exercise, or a copy of a simple app is better than an unfamiliar live business project. You need enough context to recognise when Cursor found the right file and when its suggestion has wandered. This local walkthrough requires project files on your computer. If your site exists only inside Shopify, WordPress.com, or another hosted service, use that service's export or repository instructions before continuing. Without a safe local copy, use that hosted service's own editing tools instead. ### Install Without Removing VS Code 1. Download Cursor from [cursor.com](https://cursor.com) and run the installer. 2. Choose the setup option to import your VS Code settings if you already use VS Code. 3. Keep VS Code installed while you test the extensions that matter to your work. 4. Sign in to Cursor and begin on the current Free plan unless you already need a paid feature. Imported settings can include keybindings, snippets, themes, and extensions. A keybinding connects a keyboard shortcut to an action. A snippet is a saved piece of text or code that the editor can insert for you. Cursor uses VS Code's open-source core, although some Microsoft proprietary extensions can be exceptions. Keep the old editor until you have opened the debugger, remote-access tools, and sign-in extensions your project depends on. Your project files remain ordinary files either way. ### Open a Project You Know 1. Duplicate the project folder with Finder on macOS or File Explorer on Windows so your first trial uses a copy. 2. In Cursor, choose File , then Open Folder , and select that copied folder. 3. Open the Agent side panel with `Ctrl+I`, as shown in Cursor's official quickstart. 4. Select Ask mode and type: "Explain what this project does and tell me how to run it. Do not edit files or run commands." 5. Compare the answer with any README file (the project's own introduction document), course instructions, or notes supplied with the project. If you still do not know how to run the project, pause before requesting an edit. Ask the person or service that supplied the files for the correct start instructions. The unchanged running project is your baseline, meaning the known state you will compare with Cursor's result. ### Try One Visible Change Give Cursor one task with an obvious result: "Change the home-page heading from Welcome to My Portfolio." Ask it to show the proposed change before applying anything else. This keeps the trial focused on finding one file and producing one visible result. 1. Keep the Agent side panel open and switch from Ask to Agent. 2. Type the exact visible result you want, then ask Cursor to show the proposed change. 3. Read the proposed diff before accepting it. 4. Run the project with the confirmed instructions and check the visible result. 5. Reject any unrelated file or package change. A diff is the before-and-after view of changed text. Added and removed lines are marked so you can see what the AI proposes. The diff is more reliable than a summary because it shows the change that will reach the files. ## How I Use Cursor I point Cursor's cloud agents at auto-deployed development environments, where the editor matters less than the handoff between a prompt, a running site, and the finished diff. I pair Cursor with Claude Code when a task needs a second plan or a separate review. A development environment is a working version of a project used before a public release. A deployment copies a version to a server where it can run. My setup is more advanced than a beginner needs, but the review principle is the same: check both the code change and the visible result. ## Grok Bot Grok Bot is a separate app for delegating work to bots that share a persistent cloud Linux computer assigned to your account. It adds delegated work beyond Cursor’s editor, which sits second in [our ranking](/ai-coding-tools/). Files and installed tools can remain between tasks, allowing a bot to resume a longer job. For new subscriptions, Grok Bot is included from Premium upward; the lower paid Plus tier does not include it. Existing legacy subscriptions have their own eligibility and usage rules. Check the [current feature table](https://cursor.com/pricing) before assuming any paid Cursor plan includes the same bundle. The cloud browser is separate from your computer's browser. Grok Bot can also request local command and file access, governed by its [local-computer approval policy](https://docs.x.ai/grok-bot/approvals-security-and-privacy), which defaults to asking every time. Your bots share their cloud machine; creating another bot does not give it a separate security boundary. I still like pairing delegated cloud work with [Claude Code](/ai-coding-tools/claude-code/) or [Codex](/ai-coding-tools/codex/) for local work. The useful distinction is where a task needs to run and how you will review it. Our [Grok Bot guide](/ai-coding-tools/cursor/grok-bot/) covers that handoff in detail. ## How Do Diffs and Command Approvals Differ? A diff shows file changes; command approval controls whether an agent may execute an action. Review both because a command can affect services or data beyond the files shown in the diff. A multi-file change appears as a list of affected files with additions and removals. Review the file list first because an unexpected file often reveals scope drift (the agent wandering beyond the job you asked for) before you read every line. Scope drift happens when the agent expands beyond the requested job. A small form fix may turn into a package update or a broad refactor (a rewrite of working code to change its structure). Reject the extra change and start a fresh, narrower session when the agent keeps pulling unrelated work into the diff. Command approval is separate from file review. A command can run tests, start the project, install a package, or alter stored data. Read the command and its purpose before approving it, especially when it contains words such as delete, migrate, reset, deploy, or install. > Cursor earns trust one reviewed diff and one verified result at a time. ## Which Models Can You Use in Cursor? Cursor supports its own models and selected third-party models, with access determined by your plan and account. The model interprets your request; Cursor supplies the editor, project context, tools, and review interface. Choosing a model does not change a cloud task into local model processing. Current Free and Plus plans exclude third-party models. Plus includes Composer 2.5 as well as Grok models. Premium and higher plans offer eligible third-party models with separate usage billing. Check the [model and pricing documentation](https://cursor.com/docs/models-and-pricing) alongside the picker in your account. OpenAI has announced a [proposed November 12, 2026 shutoff](https://openai.com/index/our-decision-on-cursor-following-its-acquisition-by-spacex/) for its model-supply contract with Cursor and says it is withholding future models under that arrangement. If those models are a requirement, verify the access route before choosing Cursor for the team. Keep your first model comparison small and repeatable. Run the same known task with the default model, then change only the model and repeat. Compare the changed files, test result, time, and usage so you know which part of the setup produced the difference. ![image]()This June 2026 picker is a dated snapshot. Check the live account for the current model list. ## How Much Does Cursor Cost? Cursor's current individual plans cost US$0 for Free, $8 for Plus, $30 for Premium, $100 for Super, and $200 for Ultra per month, before applicable taxes or fees. Premium is the first new plan that includes Grok Bot, cloud agents, automations, and third-party model access. Third-party usage is billed separately from the subscription. | Plan | Monthly Price | Main Distinction | | Free | US$0 | Limited Grok usage | | Plus | US$8 | Grok and Composer; no Grok Bot | | Premium | US$30 | Bot and cloud workflows | | Super | US$100 | Larger included usage pool | | Ultra | US$200 | Highest individual usage tier | | Teams | US$30–200/user | Seat tiers and administration | | Enterprise | Custom | Contracted organization requirements | The [public pricing page](https://cursor.com/pricing) describes new subscriptions. The [documentation](https://cursor.com/docs/models-and-pricing) also retains legacy Hobby, Pro, Pro+, and Ultra plans. Likewise, the [team documentation](https://cursor.com/docs/account/teams/pricing) distinguishes new Premium, Super, and Ultra seats from legacy Standard and Premium seats. Compare the plan your account actually holds. My first Cursor bill surprise came from leaving agent work on premium models and checking the usage dashboard a few days later: $60 gone. The work was useful, and I could not see the accumulating cost during the flow. That experience is why I set spending limits before enabling extra usage. New subscriptions and legacy plans meter usage differently. Check the included pool, separate third-party balance, and any automatic top-up or on-demand setting. A higher subscription does not turn separately billed model use into unlimited included work. My take: start free and pay when the visual workflow saves time on a project you understand, or when a specific paid capability earns its place. Buy a larger allowance after measuring the work you can finish and review. Vague tasks stay vague with a larger allowance. ## What Are Cursor's Main Strengths? Cursor's main advantage is continuity across the whole workspace. The file, AI conversation, diff, terminal, and running project can stay in one window. That reduces the work of copying code between a browser chatbot and an editor, and it makes the proposed change visible beside the surrounding project. Tab completion remains useful because it meets you at the smallest possible unit of work. You can ignore a weak suggestion without opening a conversation. When a repeated edit is obvious, one accepted suggestion can remove several minutes of mechanical typing. Cursor Agent is strongest on bounded changes that cross files. It can update a component and its test together, or trace a renamed value through related code. The visual file list makes that coordination easier to inspect than a long stream of terminal output. The VS Code lineage also matters for adoption. A developer can keep familiar themes, shortcuts, and many extensions while learning the AI layer in stages. That is a smaller move than learning a new editor and a new agent workflow on the same day. ## What Are Cursor's Main Limitations? Cursor can produce a large amount of plausible code before you notice that it misunderstood the task. Agent speed makes review more important because a wrong assumption can spread across several files. Keep the task bounded and stop when the changed-file list grows beyond the expected area. For a read-only investigation, select an appropriate mode and state "do not edit files or run commands." Verify the active permissions and changed-file list before continuing. Instructions express your intent; permission settings determine which actions the agent can take without another approval. Large repositories can also exceed the context selected for a session. Cursor may find a nearby pattern and miss a rule stored elsewhere. Use the [context and rules guide](/ai-coding-tools/cursor/context-and-rules/) after the basic workflow feels normal, and start a fresh session when the agent repeats a mistake. Cursor is a weaker fit for someone who needs a platform to supply the hosting, database, and deployment as part of the same beginner experience. It edits project files well, but the editor does not remove the need to understand how the project runs and where it will be published. Teams should test required extensions and account controls before replacing VS Code. The shared visual foundation does not guarantee that every proprietary Microsoft extension or organization policy will carry over. A one-project trial exposes those gaps without forcing the whole team to move. ## How Cursor Got Here Anysphere was founded in 2022 by Michael Truell, Sualeh Asif, Arvid Lunnemark, and Aman Sanger, who met at MIT. In October 2023, [TechCrunch described Cursor](https://techcrunch.com/2023/10/11/anysphere-raises-8m-from-openai-to-build-an-ai-powered-ide/) as the company's AI-native development environment and a fork of VS Code. Building on that open-source foundation gave developers a familiar editor while Anysphere added its own AI tools. Cursor's [February 2026 account of its progression](https://cursor.com/blog/third-era) describes the move from Tab completion to interactive agents and then cloud agents. That progression explains why the editor supports both small suggestions and longer delegated tasks. ## Compare Cursor The [AI coding tools comparisons](/ai-coding-tools/comparisons/) place Cursor beside the main alternatives: - [Cursor vs Claude Code](/ai-coding-tools/comparisons/cursor-vs-claude-code/) compares their editing, investigation, and review workflows. - [Cursor vs GitHub Copilot](/ai-coding-tools/comparisons/cursor-vs-copilot/) compares two editor assistants and their pricing. - [Cursor vs OpenCode](/ai-coding-tools/comparisons/cursor-vs-opencode/) compares commercial polish with an open-source agent. - [Cursor vs Codex](/ai-coding-tools/comparisons/cursor-vs-codex/) compares visual editing and delegated work across their local and cloud options. ## The Verdict Cursor is our number-two AI coding tool, behind Claude Code. The editor half of that verdict is unchanged: AI woven into a familiar VS Code-style workspace, visual diffs worth reviewing, and a Tab-to-Agent learning path a beginner can climb. What keeps it close to the top is Grok Bot arriving on the eligible plans: the bundle covers the editor you work in and agents for work you can delegate. Included usage is limited, and third-party model charges need their own budget. It even won over Mark Gates, who spent the year arguing for local models. He stays local-first, but Grok Bot's VM runs Linux, so he uses it as an isolated sandbox for OpenCode agents, off his own computer. Begin on the current Free plan with one known project, learn Tab before Agent, and pay for a paid tier when the workflow saves measurable time, then treat Grok Bot as the second half of the product. Its limits decide the rest of your stack: no browser on your own machine and a capped weekly allowance mean you still want [Claude Code](/ai-coding-tools/claude-code/) (our number one) or [Codex](/ai-coding-tools/codex/) (a close third) beside it for computer use and local control. A hosted building platform remains the better pick only when you need the database and deployment supplied for you. ## FAQ ### Is Cursor a version of VS Code? Cursor is a separate editor from Anysphere that is built from VS Code's open-source foundation. The familiar file tree, shortcuts, themes, and extension support make the move easier, while Cursor adds its own AI tools throughout the editor. ### Can a complete beginner use Cursor? Complete beginners can use Cursor, but they still need to work with project files and understand whether a change is correct. Start with a small project you can run, use one visible task, and read the diff before accepting it. A hosted no-code platform may be easier if you also need hosting, a database, and deployment supplied for you. ### What is the difference between Tab and Agent? Cursor Tab predicts the next edit while you type and waits for you to accept it. Agent takes a written task, finds relevant files, proposes changes across the project, and can run commands. Tab feels like faster typing, while Agent feels like handing over a bounded job. ### How much does Cursor cost? Cursor's new individual plans are Free at US$0, Plus at $8, Premium at $30, Super at $100, and Ultra at $200 per month, before applicable taxes or fees. Grok Bot starts with Premium; third-party model usage is billed separately. Existing legacy plans have different allowances and billing, so check the plan attached to your account. ### Does Cursor work with VS Code extensions? Cursor can import VS Code settings, keybindings, snippets, themes, and many extensions because it is built on VS Code's open-source core. Some Microsoft proprietary extensions can be exceptions, so keep VS Code installed until you test the extensions that your work depends on. ### What are Cursor cloud agents? Cursor cloud agents run a coding task on remote computers managed by Cursor instead of using your laptop for the whole job. You hand off a task, the agent works in its own environment, and you return to review the result, logs, or preview. They suit longer jobs that can proceed with occasional check-ins. ### What is Grok Bot and do I get it with Cursor? Grok Bot gives you AI teammates that work on a persistent cloud Linux computer, assigned per user and shared between your bots, where they can sign in to tools and finish longer jobs. For new Cursor subscriptions, access starts with Premium; legacy eligibility differs. Its cloud browser is separate from your own browser, and local command access follows a separate approval policy. Check the included usage before relying on it for recurring work. ## Going Deeper The [Cursor workflows guide](/ai-coding-tools/cursor/workflows/) covers the longer daily routine and VS Code migration. The [context and rules guide](/ai-coding-tools/cursor/context-and-rules/) explains how to point Cursor at relevant files and store project instructions. Cursor can also run scheduled or triggered work. The [Cursor Automations guide](/ai-coding-tools/cursor/automations/) owns that advanced topic. Finish several supervised Agent tasks before creating an automation, because a repeated unclear task only repeats the confusion faster. The [Grok Bot guide](/ai-coding-tools/cursor/grok-bot/) covers handing a bounded job to a persistent cloud computer while Tab, Agent, and your local files stay in Cursor. The [Origin guide](/ai-coding-tools/cursor/origin/) covers Cursor-hosted repos, pull requests, and when GitHub remains the remote. Pricing, model terminology, and Grok Bot access were checked against official sources on October 16, 2026. Historical examples and screenshots retain their stated dates. --- ## Best AI Coding Tools (October 2026) URL: /ai-coding-tools/ Cluster: AI Coding Tools > Overview & AI Coding Tools Claude Code is our number-one AI coding tool, Cursor is second, and Codex is a close third. Claude Code's strength in planning, debugging, and changes across a whole repository is what puts it first for us. Cursor's familiar editor and Grok Bot delegation make it our pick for visual work, while Codex is a strong alternative for handing off tasks and reviewing the results. ## Best AI Coding Tools at a Glance The order below reflects our preferences and experience at CodeWalkers. The best fit for your team can differ if you need a particular editor, local model support, or centralized administration. | Rank and Tool | Why We Recommend It | Main Trade-off | | 1. Claude Code | Our overall pick for planning, debugging, and changes across a repository | Broad file access and command execution require clear instructions and review | | 2. Cursor | Our pick for visual editing and agent work, with Grok Bot adding background delegation | Check the current bundle, model access, and separate usage charges | | 3. OpenAI Codex | A close third for delegated coding tasks and code review | Delegating more work still leaves you responsible for reviewing it | | 4. GitHub Copilot | A practical choice for GitHub teams that want to keep their editors | Its strongest fit for us is the surrounding team workflow | | 5. OpenCode | Open-source software with provider choice and local-model options | More configuration and responsibility for model costs | | 6. Google Antigravity | Worth considering for developers committed to Google's tooling | We do not use it daily, so our assessment is based on documentation and platform fit | ## What Are AI Coding Tools? AI coding tools are software applications that help developers generate, explain, debug, test, and refactor source code. They use AI models to interpret instructions and code context, while the surrounding software determines which files the model can access, which actions it can take, and how developers review the results. Some tools provide suggestions and edits inside a code editor. Terminal agents can inspect repositories, modify files, and run development commands, while cloud workflows let developers delegate tasks to a separate environment. One product can support several of these workflows. The interface, the computer executing commands, and the service processing model requests are separate parts of the setup. ## How We Ranked the Tools Our ranking starts with work that can be checked: investigating a repository, making a bounded code change, running tests, fixing failures, and reviewing the returned diff. Price, privacy, editor support, provider choice, and team controls then affect the decision. Our experience is not identical across all six products. Cursor and Grok Bot are part of the team's daily work. Claude Code gets daily project use, and we use Codex through its CLI, desktop, and review workflows. OpenCode also has a daily user on the team. Our Copilot assessment combines documentation and feature testing, while our Antigravity assessment relies on official documentation and pricing. Those differences matter. Not using a tool every day is a limit on our experience, not proof that the tool is poor. Copilot and Antigravity remain on the list because GitHub or Google integration can decide a team purchase. We give more weight to the workflow around the model than to model names alone: what context the agent can inspect, which commands it can run, how it presents a diff, and where human approval sits. A stronger model cannot rescue a workflow that hides changes or makes the wrong task easy to approve. ## The Six Best AI Coding Tools ### 1. Claude Code [Claude Code](/ai-coding-tools/claude-code/) is Anthropic's coding agent and our number one. It is our strongest pick for planning, debugging, and edits that span a large repository. Our preferred way to use it is through the terminal. Claude Code can read project instructions, inspect related files, run commands, investigate failures, and revise its work. That makes it useful for a bug whose cause and fix are spread across application code and tests. The appeal is the full investigation and repair process, rather than a single generated answer. Give it a failure to trace, ask which files it expects to change, and have it make the smallest useful edit before running the existing checks. The main limitation is the amount of access that makes this useful. An agent working across a repository needs clear boundaries, and successful command execution does not remove the need to inspect its changes. Running the client on your own machine also does not mean its model processing happens locally. For Claude Code v2.1.280 and later, Anthropic's [model configuration documentation](https://code.claude.com/docs/en/model-config) lists Opus 5.5 as the account-type default for Pro, Max, Team, and Enterprise, subject to saved model choices and organization settings. Model availability is worth checking, but it is not the main reason we prefer the workflow. Choose Claude Code when you want to investigate, debug, or refactor an established project and are comfortable reviewing command-driven work. If we could run only two tools, we would choose Claude Code plus Cursor with Grok Bot. Start with the [Claude Code workflows and safety guide](/ai-coding-tools/claude-code/workflows-and-safety/), then compare [Claude Code vs Codex](/ai-coding-tools/comparisons/claude-code-vs-codex/). ### 2. Cursor [Cursor](/ai-coding-tools/cursor/) is our number two and our pick for anyone who wants a visual editor. It keeps the familiar VS Code file tree and shortcuts, then adds agent work, visual diffs, tab completion, and cloud tasks. The editor is useful when you want to see the files an agent changes and inspect the result in the same place you develop. Its biggest recent addition is [Grok Bot](/ai-coding-tools/cursor/grok-bot/). Grok Bot is a separate app that works outside the editor. Your bots share a cloud computer assigned to your account. In our daily use, that arrangement simply works. Even Mark Gates, our local-models advocate and the most skeptical of us about hosted agents, found a use for it: he has Grok Bot run OpenCode on its Linux computer as a sandbox for agent work away from his own machine. His main workflow is still local-first on open models. That is a specific reason to choose the bundle. You can keep interactive coding in the editor and hand a bounded background job to Grok Bot. It does not make every kind of coding task equally suitable for delegation. There are limits to plan around. Grok Bot's cloud browser is separate from the browser on your computer, and included usage is limited. We still pair it with Claude Code or Codex when our work calls for a local agent. Grok Bot can also request access to local commands and files; its [local-computer access setting](https://docs.x.ai/grok-bot/approvals-security-and-privacy) defaults to asking every time. Pricing has changed since our September review. The [current Cursor pricing page](https://cursor.com/pricing) lists Premium at US$30 per month with Grok Bot included. Do not assume the earlier US$20 plan description applies to a new subscription. Check the selected plan's allowance and third-party model charges before buying. OpenAI model access is another consideration. OpenAI has announced a [proposed November 12, 2026 shutoff](https://openai.com/index/our-decision-on-cursor-following-its-acquisition-by-spacex/) for its model-supply contract with Cursor and says it is not providing future models under that arrangement. If OpenAI models are essential to your work, make their access route part of your trial. Choose Cursor when you want a visual coding environment and will use the combination of editor work and background delegation. Test it on a multi-file edit, inspect the diff, and then try a separate bounded job in Grok Bot. For the closest alternatives, read [Cursor vs Claude Code](/ai-coding-tools/comparisons/cursor-vs-claude-code/) or [Cursor vs Copilot](/ai-coding-tools/comparisons/cursor-vs-copilot/). ### 3. OpenAI Codex [OpenAI Codex](/ai-coding-tools/codex/) is a close third in our ranking. Its strongest pattern is clear delegation: give the agent a bounded task, let it work, then inspect the changes or pull request. Codex brings together terminal, desktop, cloud, and code-review workflows. For us, the appeal is handing over a coding task and coming back to changes we can check. Codex closed much of the gap to Claude Code and Cursor in our September assessment. We still rank Cursor second, but choosing Codex instead is defensible. Our preference is about the way we like to work, not a claim that Codex cannot handle local projects or that Claude Code has only one interface. The main constraint is review capacity. An agent can generate more work than you have time to check. Keep tasks small enough that you can understand what changed, why it changed, and whether the project's tests support the result. Use one task for implementation and a separate pass for review, then check both against the repository tests instead of letting either agent approve the other. Available models, cloud access, and usage allowances depend on the account and plan. Choose Codex when you want delegated implementation and review to be central to your workflow. Start with one clearly scoped task and judge the returned diff, not just the agent's description of its work. Our [Codex desktop guide](/ai-coding-tools/codex/codex-desktop/) explains the workflow, while [Cursor vs Codex](/ai-coding-tools/comparisons/cursor-vs-codex/) compares the two approaches. ### 4. GitHub Copilot [GitHub Copilot](/ai-coding-tools/copilot/) remains a practical choice for a team that already works in GitHub and wants centralized administration without changing editors. It has dropped down our ranking. Nobody on our team uses the VS Code and Copilot pairing daily anymore, and the last person who did moved to OpenCode and Grok Bot. That is our experience, rather than a reason every team should make the same move. Copilot's advantage is the surrounding GitHub workflow: editor assistance, issue-to-pull-request work, code review, and administration. If those are already part of how your team ships software, keeping them together can matter more than our preference for another agent. The [current individual plans](https://github.com/features/copilot/plans) include Free and Pro at US$10 per month. Chat, agent work, and review consume GitHub AI Credits, so compare the included allowance and spending controls as well as the subscription price. Choose Copilot when keeping your existing editor and GitHub-managed workflow is the priority. Its most revealing trial starts with an issue that already has acceptance criteria. Assign it to the cloud agent, inspect the pull request, and compare the review effort with doing the same work through the editor. That shows whether the GitHub handoff helps your team or adds another queue someone must supervise. See our [Copilot setup and enterprise guide](/ai-coding-tools/copilot/setup-and-enterprise/) for rollout considerations. ### 5. OpenCode OpenCode is the open-source option for developers who want to choose their model provider, inspect the tool, or use local models. The software and the model are separate parts of the setup. Free software does not automatically mean free inference. A hosted provider may charge for usage, while running a model locally requires suitable hardware. OpenCode's [provider documentation](https://opencode.ai/docs/providers) includes hosted services and local configurations. That flexibility also adds setup work, which is why we rank OpenCode below the more polished paid tools for most readers. It earns its place when control over the provider and configuration is useful enough to justify that work. Mark Gates uses OpenCode with open models in his everyday local setup. He also has Grok Bot run it on its Linux cloud computer for tasks he wants kept away from his own machine. Those are different deployments. The second is an off-machine workspace, not local processing on his personal computer. Before comparing model quality, check where credentials are stored, which services receive code, how edit and shell permissions work, and what happens if the provider is unavailable. Choose OpenCode when provider choice or local-model support is a requirement, and you are willing to manage the configuration. Compare it with [Claude Code](/ai-coding-tools/comparisons/claude-code-vs-opencode/), or use our [advanced OpenCode guide](/ai-coding-tools/opencode/advanced/) to explore the setup. ### 6. Google Antigravity [Google Antigravity](/ai-coding-tools/antigravity/) is the option we would examine for a developer already committed to Gemini and Google's development tooling. Nobody at CodeWalkers uses Antigravity daily. Its place in this ranking comes from platform fit and our review of its documentation, rather than a claim of sustained first-hand use. We would trial it on the Google-specific project that brought it onto the shortlist, such as a Firebase-backed feature or an Android project. A generic JavaScript demo will not tell you whether that fit saves time in your own work. Keep the comparison fair: give Antigravity and the alternative the same repository, acceptance criteria, and test command. Compare the changes and the review required, rather than assuming a product's connection to Google makes it better for every Google-related task. Google's [changelog](https://antigravity.google/docs/changelog/) and [pricing page](https://antigravity.google/pricing) are the places to check current capabilities and allowances. Choose Antigravity for a trial when Google's tooling is a fixed part of your project and you want to evaluate that fit directly. We still prefer Cursor as the general-purpose editor. Our [Cursor vs Antigravity comparison](/ai-coding-tools/comparisons/cursor-vs-antigravity/) explains the decision in more detail. ## How Should You Choose an AI Coding Tool? Choose according to the work you need to complete, the environment you already use, and the changes you can realistically review. An editor is useful when you want inline suggestions and visible changes beside your source files. A terminal workflow suits developers who want an agent to run commands and investigate test failures. Cloud execution is useful for work you can delegate to a separate environment. These are overlapping capabilities, not fixed product categories. Compare the interface you use separately from the computer that executes the task and the service that processes the model request. For a first trial: - Start with Claude Code if you want our overall recommendation. Choose Cursor instead if you prefer working visually with files and diffs. - Compare Claude Code and Codex if repository investigation, multi-file changes, and delegated tasks are your priority. - Keep Copilot on the shortlist if GitHub administration and your existing editor are requirements. - Choose OpenCode to evaluate provider control if local models or configurable inference matter most. - Trial Antigravity on a relevant Google project if platform fit is what brings it into consideration. Beginners should start with a small, visible result before granting broad command access. The [getting-started guide](/ai-coding-tools/guides/getting-started/) covers that first session. Solo developers can use our [solo developer guide](/ai-coding-tools/guides/best-for-solo-devs/) to compare a one-tool setup with an editor-plus-agent combination. ## What Will You Pay Beyond the Subscription? Your total cost can include the subscription, additional usage, separate model charges, and the time spent reviewing and correcting generated code. Check four things before comparing prices: 1. Billing basis. An annual plan advertised as a monthly equivalent is a different commitment from monthly billing. 2. Included usage. Identify which models and actions share the allowance, and when it resets. 3. Additional charges. Check API billing, prepaid credits, automatic top-ups, and spending limits. 4. Feature eligibility. Confirm that the plan includes the cloud, review, or team features you intend to use. Cursor illustrates why this matters. Its current pricing differs from the plans described in our September review. The [team documentation](https://cursor.com/docs/account/teams/pricing) distinguishes new Premium, Super, and Ultra seats from legacy Standard and Premium seats, and explains separate third-party model charges. OpenCode illustrates a different distinction: the agent software may be free while a hosted model still costs money. A local model avoids that provider's API bill, but it does not eliminate hardware requirements. Claude plans now include monthly [API credits](https://platform.claude.com/docs/en/about-claude/api-credits-for-subscribers): US$100 on Max 5x, US$200 on Max 20x, and US$20 or US$100 per Team seat, pooled up to US$500. They pay for the Claude API and Agent SDK, not Claude Code usage. Unused credits expire each billing cycle. For a budget-led shortlist, use the [free AI coding tools guide](/ai-coding-tools/guides/free-tools/) and check the current limits of each option. ## How Should You Test Your Shortlist? Test two tools on the same repository and compare the effort required to reach a correct, reviewable result. A seven-day trial gives you better evidence than a feature list: 1. Day One: Ask a codebase question whose answer you already know, then check the cited files. 2. Day Two: Make one focused edit and inspect every changed line. 3. Day Three: Run a multi-file task with a named test command and a strict file boundary. 4. Day Four: Try the review workflow on a pull request containing one known problem. 5. Day Five: Check usage, credits, or API cost in the vendor dashboard. 6. Day Six: Repeat the hardest task with the nearest competing tool. 7. Day Seven: Compare review time, failed checks, unexpected files, and policy friction. Do not score only the first generated answer. Count the time needed to correct it, the number of unexpected changes, and whether another developer can inspect the result. The best tool is the one that reduces total work without leaving you with changes you cannot confidently review. ## Our Verdict Claude Code is our top pick. It does the deepest work across a real repository: tracing a failure, planning the change, and running the checks before you review the result. Cursor is second, with Codex a close third. Cursor with Grok Bot gives you a visual editor for interactive work and a separate cloud workspace for jobs you can hand off. If we could run only two tools, we would choose Claude Code plus Cursor with Grok Bot. Choosing Codex as the companion is also defensible, especially if its delegation and review workflow suits you better. OpenCode is the choice when control over models and providers matters more than convenience. Copilot stays relevant for GitHub-administered teams that want to keep their existing editors. Antigravity belongs on the shortlist when Google's tooling is central to the project. Once your shortlist is down to two, use our [AI coding tool comparisons](/ai-coding-tools/comparisons/) and test both against the same task. ## FAQ ### What programming languages do AI coding tools support? AI coding tools support common languages such as Python, JavaScript, TypeScript, Java, C#, Go, and Rust. The quality of their suggestions varies by model, language, framework, and available project context. Test the tool on your own code rather than treating language support as a guarantee of correct output. ### Can AI coding tools build a complete app? AI coding tools can generate and connect much of a small app, but they do not reliably deliver a production-ready application without human oversight. You still need to define requirements, configure real services, test behavior, review security, and maintain the deployed application. ### Will AI coding tools replace developers? AI coding tools can automate parts of a developer’s work, but that does not establish that they will replace developers as a profession. Developers remain responsible for requirements, technical decisions, verification, and release approval. The longer-term effect on jobs is uncertain. --- ## Complete Guide to OpenCode (2026) URL: /ai-coding-tools/opencode/ Cluster: AI Coding Tools > Overview & AI Coding Tools OpenCode is an open-source AI coding agent with terminal, desktop, and IDE interfaces. I value its terminal workflow because it sits beside the editor I already use. That makes it a different kind of AI coding tool: one terminal agent can work with VS Code, Neovim, JetBrains, or Emacs, and you can swap model providers without rebuilding your daily setup. That freedom sounds abstract until a provider shifts a price, access rule, or model list. OpenCode keeps the editor, model, and agent separate, so you can move one part without throwing out the rest of the way you work. Version scope: The commands, configuration examples, and permission defaults below describe OpenCode 1. OpenCode 2 supports much existing configuration, but installation, server workflows, and some defaults differ. Read the [V2 migration guide](https://opencode.ai/v2/docs/migrate-v1/) before converting a setup. Both versions use the same `opencode` command, so their default installations can replace one another. See our [AI coding assistant comparison](/ai-coding-tools/) for how OpenCode fits alongside the other tools we cover. ## Who Is OpenCode For? OpenCode is for developers who want to choose their model provider, run local models, or configure a terminal-based coding agent and are willing to manage that setup. | Decision | OpenCode Answer | Best Fit | | Working style | Terminal-first agent beside your editor | Developers who already use CLI tools | | Cost | Free tool; model costs vary | People willing to choose their own cost route | | Privacy | Local inference through Ollama is available | Code that must stay on controlled hardware | | Model choice | 75+ providers plus local models | Workflows that cannot rely on one vendor | | Main trade-off | More setup, no inline autocomplete | Developers who value control over polish | ## What Does OpenCode Do? OpenCode reads project context, proposes or makes code changes, and runs tools through a coding-agent interface connected to your chosen model provider. This overview focuses on the terminal workflow because that is where its provider flexibility and remote-work fit are easiest to see. OpenCode reads project files, proposes edits, runs approved commands, and writes those edits back to the same repository your editor has open. OpenCode leaves inline autocomplete to a separate editor tool. Pair it with Copilot if you want ghost-text suggestions, or use [Cursor](/ai-coding-tools/cursor/) when the editor and agent should be one product. That separation is the main reason to choose OpenCode. Your editor can be VS Code, Neovim, JetBrains, or Emacs, while the model behind OpenCode can vary by task. The configuration stays in readable files, and the agent remains usable when a provider alters its price, access rules, or model lineup. That portability matters most on mixed projects. The same terminal workflow can follow you from a local repository to a remote SSH session, while the editor and model vary around it. ## Get Started with OpenCode 1 Install OpenCode from its official site, open a project directory, connect an approved provider, and use Plan mode before allowing an edit. Read the proposed file list and command before approval. The [advanced OpenCode guide](/ai-coding-tools/opencode/advanced/) explains the setup files, provider connection, permissions, and first-task loop. Use a known module or failing test for the initial trial and run the focused check yourself. ## Permissions That Match the Task OpenCode's [permission system](https://opencode.ai/docs/permissions) can allow, ask, or deny individual tool classes. The documented keys cover reading, editing, search, shell commands, subagents, external directories, web access, language-server tools, and skills. A permission can also use file or command patterns, so the boundary can be narrower than "shell on" or "shell off." Start from the task, then grant only what it needs: - A review agent can read, search, and use the language server while edits and shell commands stay denied. - A test repair can allow edits in test files and ask before the focused test command runs. - A documentation task can deny package managers, deployment commands, and access outside the repository. - A normal coding agent can ask before edits and shell commands until its plan matches the requested scope. The approval prompt is useful only when you read it. Check the full command, working directory, and affected path before allowing a shell action. A safe-looking test command can still call a project script that resets data or rewrites generated files. Put permanent team restrictions in configuration and keep one-off boundaries in the task itself. OpenCode also exposes a `doom_loop` permission that asks whether an identical tool action may continue after three repeated calls. Approval is not proof that the next attempt is better. Repeated edits to the same file or repeated failures from the same command usually mean the task needs a smaller scope, a different model, or a human decision. ## How Much Does OpenCode Cost? The OpenCode client is free; hosted inference, optional subscriptions, and local hardware have separate costs. [OpenCode works with more than 75 providers](https://opencode.ai/docs/providers/) through Models.dev, including direct vendor APIs, OpenCode's own plans, and local models. That range is useful only when you choose a cost path deliberately. The provider documentation describes connection options, not a single shared data policy or allowance. A direct API account keeps billing and retention with its vendor, while an OpenCode-managed route adds another service layer. Record the provider, selected model, and account owner in the project decision before a task receives client code. - Bring your own API credential for direct billing and the provider's current model catalog. Cost rises with token use, so set provider-side limits before long agent sessions. - OpenCode Zen is the optional pay-as-you-go gateway for models the OpenCode team has tested with the agent. - OpenCode Go costs $10 monthly, with Go Plus at $40 monthly. These optional hosted plans have usage limits; they are not required to install the client. - Local Ollama removes the per-token API bill. Hardware, electricity, speed, and model quality become the constraints instead. [Moonshot launched Kimi K3 on July 16](https://www.kimi.com/blog/kimi-k3) as a 2.8-trillion-parameter MoE flagship with a 1-million-token context, aimed at long engineering sessions and large repositories. Check the current provider catalog for availability and usage limits. [Z.ai released GLM-5.2 on June 16](https://z.ai/blog/glm-5.2) for the same long-horizon engineering class. Its [official model card](https://huggingface.co/zai-org/GLM-5.2) reports 753 billion parameters, a 1-million-token context, and an MIT license. The full hardware and availability analysis lives in the [OpenCode local-model guide](/ai-coding-tools/opencode/local-models/). ### Choosing a Provider Route Direct provider access is easiest to audit when your organization already has an approved vendor account. Billing, retention terms, model availability, and spend controls remain with that provider. The trade is administrative duplication if you want several vendors, because each credential and policy needs its own owner. [OpenCode Zen](https://opencode.ai/docs/zen/) offers a curated pay-as-you-go route, while [OpenCode Go](https://opencode.ai/go) offers subscriptions with selected hosted models. These reduce provider setup, but they add OpenCode's service layer between the agent and the model host. Read the current retention and billing terms before sending client code. Local Ollama replaces the hosted inference account with hardware and operating costs. Check whether the model fits available RAM or VRAM, how quickly it completes a representative task, and whether its tool use is reliable enough for edits. A smaller model that finishes a focused test repair can be more useful than a large model that swaps into memory and stalls. Keep a fallback model in the configuration only when you understand what changes with the switch. A second provider may have different data terms, tool support, context limits, or model names. Silent failover can cross a policy boundary even when the generated code looks the same. Compare providers with one repository task that has an objective result. A useful candidate is a failing unit test whose fix touches one implementation file and one test file. Give each model the same `AGENTS.md`, permissions, prompt, and command. Compare the returned diff, the number of retries, the test result, and the billed usage. Judge the model by whether it finds the relevant code, stays inside the boundary, and gives you a result you can verify. Long explanations and large patches carry no extra credit. If two providers perform similarly, retention terms, cost controls, and reliability should settle the choice. Keep the comparison reproducible. Use the same repository revision, `AGENTS.md`, permission profile, prompt, and focused check for each provider. Record an unavailable model or a rejected command as a result rather than changing the setup until one provider succeeds. That preserves the difference between model capability, permissions, and a broken credential. ## Where Does OpenCode Send Your Code? OpenCode sends model requests to the provider you configure. A local Ollama endpoint keeps inference on your machine; a hosted API sends selected context to its service. OpenCode Go retention and training policies vary by model, so check its [model-specific privacy table](https://opencode.ai/v2/docs/console/go/). Do not treat every Go model as zero-retention or excluded from training. Ollama can keep model inference on your machine, but "local" does not disable every network path. Web search, remote MCP servers, package managers, Git hosting, and a second cloud provider can still send data elsewhere. Review the whole tool configuration before treating a session as offline or suitable for regulated code. A local-model claim should therefore start with a network map, not a product name. Disable or account for web tools and remote MCP servers, inspect Git remotes and package registries, and confirm that the selected provider is the local endpoint. The [local-model guide](/ai-coding-tools/opencode/local-models/) covers hardware requirements and configuration. ### Provider Ownership Is a Team Decision Provider flexibility does not remove ownership. Someone still needs to approve credentials, set a spend cap, review retention terms, and decide whether a fallback model can receive the same code. OpenCode makes those choices visible in configuration, which is useful only when the team is prepared to maintain them. Use OpenCode's [permission documentation](https://opencode.ai/docs/permissions), to define the boundary in the agent configuration. A project can allow read and search access while asking before edits or shell commands. That split supports a review task without granting the same reach as an implementation task. Review the effective permission set after configuration changes, not only the task prompt. Review the effective permissions when the task changes. Instructions can ask an agent to stay within a scope, but configuration and external access controls determine what it is technically able to do. A practical privacy review follows the data instead of the product label: - Identify the active model provider and where its credential is stored. - List every enabled MCP server, web tool, package registry, and Git remote. - Check whether logs, prompts, or task traces are retained outside the repository. - Run a network-isolated test if the requirement is genuinely offline operation. ## Limits and Trade-Offs OpenCode asks you to understand providers, usage limits, permissions, and model hardware. Inline completion still needs a separate editor tool, and provider behaviour varies. Model-specific tool calling, authentication, rate limits, and error messages remain part of the daily workflow. Provider flexibility also creates more support work. A team must decide which model combinations are approved, who owns credentials, how costs are capped, and what happens when a provider removes a model. Open configuration makes those decisions inspectable while leaving the support work with the team. ## OpenCode Verdict OpenCode accepts more setup in return for provider choice, readable configuration, remote work over SSH, and a route to local inference. That trade-off is justified when model access, data location, or vendor dependence is an active requirement; otherwise an integrated editor or a first-party terminal agent reduces the operating burden. ## FAQ ### Is OpenCode free? OpenCode itself is open source and free to install. Your model can still cost money: direct provider APIs and OpenCode Zen charge for usage, while OpenCode Go starts at $10 monthly, with Go Plus at $40 monthly. A local Ollama model has no per-token API charge. ### Can OpenCode run local models with Ollama? OpenCode can use local models through Ollama. Its provider documentation includes a local configuration using Ollama's OpenAI-compatible endpoint at localhost. Ollama handles inference on your machine, and OpenCode remains the agent interface. Model size still has to fit your hardware, and other enabled tools or providers can create separate network traffic. ### OpenCode vs Claude Code: when does each fit? Choose OpenCode when model-provider choice or local inference is a requirement; choose Claude Code when you prefer Anthropic’s integrated coding-agent workflow. Compare the provider, data path, configuration burden, and review controls for the repository in scope. ### Which open models work best with OpenCode in 2026? GLM-5.2 is a strong OpenCode model choice for long engineering work, with a 1-million-token context and MIT-licensed weights. Kimi K3 is the newer 2.8-trillion-parameter option available through OpenCode Go. For Ollama on one workstation, use a smaller quantized coding model that fits your RAM or VRAM instead of either datacenter-scale flagship. ### Do I need API credentials to use OpenCode? OpenCode needs credentials for whichever hosted provider you choose. Direct Anthropic, OpenAI, Google, Moonshot, or Z.ai access uses that vendor's credential; Go and Zen issue OpenCode credentials after signup. A local Ollama setup can run without an external model-provider credential, which is the cleanest zero-credential path. ## Compare OpenCode - [Cursor vs OpenCode](/ai-coding-tools/comparisons/cursor-vs-opencode/) compares an integrated commercial editor with a model-flexible open-source agent. - [Claude Code vs OpenCode](/ai-coding-tools/comparisons/claude-code-vs-opencode/) compares two terminal agents with different provider and ownership choices. - [AI coding tools comparisons](/ai-coding-tools/comparisons/) covers the wider market. ## More on OpenCode Read [OpenCode local models with Ollama](/ai-coding-tools/opencode/local-models/) for hardware, current open models, and the privacy boundary. Read [advanced OpenCode agents and automation](/ai-coding-tools/opencode/advanced/) for permissions, MCP servers, custom agents, and bounded long-running loops. Read [running many OpenCode sessions at once](/ai-coding-tools/opencode/session-fleets/) for worktree isolation, remote clients, and headless scripting. Read [OpenCode for regulated and enterprise teams](/ai-coding-tools/opencode/enterprise/) for the outbound-path map, self-hosting, and what procurement will ask for. Pricing and privacy corrections checked October 16, 2026; client workflow examples refer to OpenCode 1. --- ## Getting Started with the Claude Desktop App URL: /ai-coding-tools/claude-code/claude-desktop/ Cluster: AI Coding Tools > Claude Code The Claude desktop app puts Claude chat, Cowork, and Claude Code in one desktop window. Code supports macOS and Windows, with Linux available as a beta for Ubuntu and Debian. The Code tab is the no-terminal entry point for letting Claude read a project, edit files, run checks, and show you a visual diff. You still need to choose the right folder and read every proposed edit. File changes remain real work, even when the interface looks like chat. I use Claude Code Desktop's Browser pane for visible app testing, and I keep the diff beside that preview because the rendered page and changed files answer different review questions. ## What Is Claude Code Desktop? Claude Code Desktop is the Code workspace in the Claude desktop app. It connects a conversation to a project folder so Claude can inspect files, propose changes, run development tools, and show the resulting diff. You do not need to install the separate Claude Code CLI to start. The app includes its own coding runtime; your project may still need Git, language runtimes, or other development tools. A terminal is a text window for running commands. Desktop can show command output while letting you direct the task and review changes through the visual interface. ## Choose Chat, Cowork, or Code Claude Desktop has three work areas, and they do different jobs. Chat is for questions and writing that do not need access to a project folder. Cowork can take a broader task and work through it in its own environment. Code opens Claude Code through a visual interface and ties the session to a folder or repository you choose. Use Code for a bug fix, a small feature, or a test run where the agent must inspect the project. A local Code session works with files and developer tools on your machine. A remote session runs in an Anthropic-hosted environment and can keep working after you close the app. The [desktop reference](https://code.claude.com/docs/en/desktop) also documents SSH sessions for projects that live on another computer. Computer use belongs to a separate desktop feature. It lets Claude control approved desktop apps when a connector or shell command cannot do the job. Anthropic calls it a research preview, makes you turn it on in Settings, and asks before it accesses an app. Leave it off for your first coding session. The Code tab already has the file, terminal, diff, and preview tools needed for normal project work. ## Create Your Account 1. Open [claude.ai](https://claude.ai) in any web browser to get started. 2. Choose Sign up , and register with your email address or a Google account. 3. Confirm your email if it asks you to, and you are in. That free account already lets you chat with Claude, in the browser and in the desktop app, which is a fine way to try it before you pay for anything. ## Pick a Plan The app itself is free to download and use for chat on any account. The Code tab and Cowork require a paid plan. Prices were checked on October 16, 2026, so check the [official pricing page](https://claude.com/pricing) before you commit. | Plan | Price | Good for | | Free | $0 | Chat only | | Pro | $200/year / $20 monthly | Code and Cowork | | Max 5x | $100/mo | Five-times Pro usage | | Max 20x | $200/mo | Twenty-times Pro usage | Free covers ordinary Claude chat. Pro is the least expensive individual plan that includes both Code and Cowork. Max raises the usage allowance to five or twenty times the Pro level and adds priority access during busy periods. Begin with Pro unless you already know that long, daily coding sessions will exhaust its limits. Plan limits are shared across Claude products, and Anthropic can revise prices or access. Treat the table as a dated buying snapshot. The model menu can also differ by account, so choose from what the app shows instead of following an old model list. ## Install the App 1. On [claude.ai](https://claude.ai), look for the Download option (in the menu or your account settings) and grab the app for your computer. The Code workspace supports macOS, Windows, and a Linux beta for Ubuntu and Debian. 2. Open the file you downloaded and follow the normal install prompts, the same as any other app. 3. Launch it and sign in with the account you just made. The coding runtime is included. Install any development tools your chosen project requires; Windows local sessions also require Git. ## Start Your First Code Session Open the Code tab, select Local, and choose a small project folder. Pick a project that is already in Git and has no uncommitted work. The [official desktop quickstart](https://code.claude.com/docs/en/desktop-quickstart) recommends a project you know well because you can judge its plan and edits. Start with a read-only inspection tied to the project: > Find the page that renders the contact form. Explain which file controls its labels and how I can preview it. Do not change files yet. After checking the answer, give Claude one bounded edit: > Change the contact form's submit label to "Send message." Keep its behavior unchanged, run the relevant build, and show the diff and browser preview. Set the permission mode to Plan if you want Claude to inspect the folder before it edits. Ask it to name the files it expects to touch and the check it will run when done. For a web project, that check might be one test command, a build, or an app preview. A task without a pass-or-fail check leaves you to decide from a confident-sounding message. ## Review Changes Before Keeping Them The desktop interface earns its keep after Claude starts editing. Open the diff pane and read each changed file. Green lines are additions and red lines are removals. If a file has no clear link to your request, ask why it changed before you accept the work. Use the preview pane for a web app. Claude can start a development server, open the page, inspect the page structure, click controls, and take screenshots. A passing build proves that the code compiles. Layout and form behavior need a preview check as well, so ask for both results when the task affects a user interface. Claude Code checkpoints track changes made through its direct file-editing tools. They do not track file changes made through Bash commands, or undo database writes, deployments, or payments. The CLI's Escape-twice and `/rewind` controls are documented in the [checkpoint guide](https://code.claude.com/docs/en/checkpointing). Git remains the stronger boundary: begin clean, inspect `git diff`, and commit only after you understand the result. ## Troubleshoot Common Desktop Problems - The Code tab asks you to upgrade: confirm the account has a paid Pro, Max, Team, or Enterprise plan. - You see a 403 error: sign out, sign in again, restart the app, and confirm the paid plan is active. - A command is missing: open the Local environment settings and check the PATH seen by the app. - The preview loses your login: turn on Persist sessions in the preview menu so cookies and local storage survive server restarts. - Windows cannot start a local session: install Git, then restart Claude Desktop. Update the app before chasing a stale bug. On macOS use Claude → Check for Updates; on Windows use Help → Check for Updates. The desktop reference keeps a versioned changelog and lists platform limits, including the Linux beta and the absence of inline autocomplete. ## A Peek at the Terminal The desktop app is the gentle on-ramp, and most people are happy here for a good while. The next step up, when you want it, is the terminal: that plain text window where you type commands instead of clicking. Anthropic's [Claude Code](/ai-coding-tools/claude-code/) runs there, with access to your whole project at once, which is what makes it good for bigger, more automated jobs. You do not need any of that yet. Get comfortable inside the app first, with no rush to move on. When the app starts to feel like the limit rather than the helper, that is the signal to read the Claude Code guide and try the terminal version. ## When Desktop Is the Right Starting Point Claude Desktop is a sound first step when you want Claude Code's project access and visual review without living in a terminal. Start with one clean test project, stay in Plan mode until the proposed scope makes sense, and keep the diff and preview panes open. Move to the CLI when scripting and terminal speed become more useful than the desktop workspace. ## FAQ ### Does Claude Code Desktop need the command line? Claude Code Desktop does not require command-line knowledge. Its Code tab includes the coding agent, a file view, a visual diff, an app preview, and an integrated terminal, but you can begin without typing terminal commands. The separate Claude Code CLI is only needed if you want to run Claude from your own terminal or use scripting flags. ### Can I use Claude Code Desktop for free? Claude Code in the desktop app requires a paid plan; the Free plan supports ordinary Claude chat. The Code tab and Cowork require a paid Pro, Max, Team, or Enterprise plan. Pro costs $20 month to month or $200 billed annually, advertised as about $17 per month. ### What folder should I open first? Beginners using Claude Code Desktop should open a small project they know and can restore from Git. Avoid your home folder, a directory full of private files, or a production checkout. A familiar test project makes it easier to judge the plan, read the diff, and spot a file Claude should not have touched. ### Is computer use the same as the Code tab? Claude computer use and the Code tab are different features. The Code tab works through coding tools tied to the project folder you select. Computer use can click and type in approved desktop apps when no connector or command-line tool fits. Anthropic labels computer use a research preview, keeps it off by default, and asks for app access. ## Where to Go Next - [Claude Code guide](/ai-coding-tools/claude-code/): the terminal version, for more control and automation once you are ready. - [Beginners guide to AI coding tools](/ai-coding-tools/guides/getting-started/): how these tools actually work under the hood, in plain English. - [Best AI coding tools](/ai-coding-tools/): see how Claude stacks up against Cursor, Codex, and the rest, so you can pick what fits. Desktop setup, platform support, and pricing checked against official documentation on October 16, 2026. --- ## Claude Code: Configuration & Context URL: /ai-coding-tools/claude-code/configuration-and-context/ Cluster: AI Coding Tools > Claude Code CLAUDE.md gives [Claude Code](/ai-coding-tools/claude-code/) persistent project instructions: build commands, coding conventions, and decisions it should carry into each session. Keep shared instructions in the repository, personal preferences in your user file, and enforceable restrictions in permissions or hooks. I stopped re-explaining recurring Claude Code rules once a reviewed CLAUDE.md and a narrow hook could carry the instruction and enforcement separately. Before investing in a Claude-specific setup, compare the broader [AI coding tools](/ai-coding-tools/) lineup to check that its workflow fits your project. For instructions that need an enforceable check, read [Claude Code hooks and tool connections](/ai-coding-tools/claude-code/mcp-and-hooks/); use the [workflow and safety guide](/ai-coding-tools/claude-code/workflows-and-safety/) to choose the surrounding approval and review process. ## What Does CLAUDE.md Do? Claude Code loads CLAUDE.md as instructions when a session begins. The file can name build commands, code conventions, architecture decisions, repository etiquette, and traps that are hard to spot from source. It is Markdown, so the best version reads like a short briefing rather than a machine schema. Run `/init` to generate a starter from the repository, then edit it. Run `/context` to confirm the file loaded. Anthropic's [memory guide](https://code.claude.com/docs/en/memory) says CLAUDE.md files load in full, which is why every line needs to earn space. Do not put secrets in any project instruction file. A shared CLAUDE.md is committed to Git, and even a private local file is model context. Store tokens in the secret system used by the project and refer to the variable name only when Claude needs to know it exists. ## Choose the Right Scope | Scope | Location | Who Gets It | | Managed | Operating-system policy path | Everyone in the organization | | User | `~/.claude/CLAUDE.md` | You, in every project | | Project | `./CLAUDE.md` | The team through Git | | Local | `./CLAUDE.local.md` | You, in this project | Put personal defaults in the user file and repository facts in the project file. A local file fits machine paths or a temporary personal rule that teammates should not inherit. Add `CLAUDE.local.md` to `.gitignore` before using it. Claude loads broader instructions before narrower project instructions. A project can refine a user preference, but conflicting rules create drift. Remove the conflict or state the exception in one place. In Claude Code v2.1.277 or later, supported sessions can load `AGENTS.md` when no project `CLAUDE.md`, `.claude/CLAUDE.md`, or `CLAUDE.local.md` exists in the working directory or above it. Adding a local Claude file can therefore stop that fallback. Use the Project instructions setting in `/config` to load both formats when needed, then confirm what loaded. ## Write a Useful Project Brief Start with facts the agent would otherwise have to rediscover or guess. Name the package manager, test commands, branch lane, generated paths, and one or two source files that show the house pattern. Skip file-by-file maps and standard language advice Claude already knows. ```markdown # Acme Storefront ## Commands - Install with `pnpm install` - Run focused tests with `pnpm test -- ` - Run `pnpm build` before a commit ## Repository Rules - Work on `feature/*` branches from `development` - Ask before adding a package - Do not edit generated files under `src/api/client/` ## Patterns - Follow `src/server/routes/orders.ts` for API handlers - Read `docs/auth.md` before editing session code ``` "Follow best practices" is too broad to alter behavior. "Follow `src/server/routes/orders.ts` for API handlers" gives Claude code it can inspect. A rule such as "ask before adding a package" names a decision point. Test each line by asking what mistake it prevents; remove it if the answer is unclear. Treat the instruction file like any other reviewed source file. Review it when Claude repeats a mistake, when commands move, or when the branch process shifts. Avoid appending every correction from one isolated session. First decide whether the lesson belongs in a shared instruction, a path rule, a reusable skill, or only the current prompt. ## Split Detail with Imports and Rules CLAUDE.md can import another file with `@path/to/file`. Use imports for short project docs that apply to every session, such as Git rules or a compact architecture map. Imported text also uses context, so an import is not a way to hide a large manual from the context window. ```markdown # Additional Instructions - Git workflow: @docs/git-instructions.md - Architecture map: @docs/architecture-summary.md ``` Imports also suit a repository shared with other [AI coding agents](/ai-coding-tools/). Codex reads `AGENTS.md`, so a CLAUDE.md that contains `@AGENTS.md` lets both tools follow one brief. Path-scoped rules fit detail that matters only when Claude works in part of the tree. Put them under `.claude/rules/` and add frontmatter patterns for the paths they cover. An API rule can load for `src/api/**/*.ts` without taxing a CSS task. The root file stays an index of always-needed facts, while auth, data, UI, and release detail sit near the code or path rule that calls for them. Keep each source owned by one domain so two docs do not give Claude different answers. ## Separate Instructions, Memory, and Skills - CLAUDE.md: instructions you author and review for every matching session. - Auto memory: machine-local facts Claude records while you work. - Skills: topic knowledge or repeatable routines loaded when needed. - Settings: permissions, hooks, plugins, model choices, and tool behavior. Claude stores auto memory under `~/.claude/projects/` on your machine. Claude loads the first 200 lines or 25 KB of its MEMORY.md index, whichever comes first, and reads topic files when needed. Run `/memory` to inspect or edit it. Memory is local to the machine; team documentation still belongs in Git. Use a skill when the full text is relevant only to a class of tasks. A release routine, security review, or API convention can live in `.claude/skills//SKILL.md`. Claude sees the skill description at startup and loads the full instructions when the task calls for them, which keeps the base context smaller. ## Manage the Session Context The context window contains far more than your prompt. It holds conversation history, files Claude read, command output, instruction files, auto memory, loaded skills, and tool definitions. Long build logs and broad repository searches can consume more space than the request itself. Run `/context` when answers begin to lose earlier decisions. Run `/clear` whenever you switch from one task to an unrelated task. Use `/compact focus on the API changes` when you need a summary that preserves one thread. You can also add compact instructions to CLAUDE.md for facts that must survive a long session. Claude clears old tool output and compacts the conversation near the limit, but a summary can omit a detail. A fresh session with a short handoff often beats a long rescue attempt. Put persistent rules in files, record the current state in Git, and start the next task with only the sources it needs. ## Configure Large Codebases by Domain A large repo should not have one giant instruction manual. Keep the root CLAUDE.md for commands and rules shared across the tree. Add child CLAUDE.md files or path rules for packages that have their own build steps, data models, or review checks. Point to code that already carries the pattern. For an auth task, name the auth rule or doc and one handler that shows the expected flow. Ask a subagent to map an unfamiliar package if that search would flood the main session with file reads. The subagent returns a summary while keeping its investigation in a separate context. Select context even when the model advertises a large window. A relevant test, type, and handler beat hundreds of unrelated files. The [Claude Code architecture guide](https://code.claude.com/docs/en/how-claude-code-works) explains that tool output and loaded extensions share the same finite context as your request. Give each package one owner for its instructions. If both the root file and a package file describe the same test routine, one copy will go stale. Keep the shared rule at the root and the package exception beside that package. A short pointer is safer than two full versions of the same policy. ## Troubleshoot Instruction Drift - Run `/context` to confirm which instruction files loaded. - Run `/memory` and check whether auto memory conflicts with the project rule. - Search for the same instruction in user, parent, project, local, and path-scoped files. - Rewrite vague rules as an action, boundary, command, or pointer to a source file. - Move a must-hold boundary to a hook, permission rule, sandbox, or CI check. Test a revision with a small task that used to fail. If the agent still misses it, adding capital letters may make the file louder without making it clearer. Cut nearby clutter, remove conflicts, and state the rule beside the command or path it governs. ## Keep Configuration Reviewable Keep CLAUDE.md short enough to review and specific enough to alter behavior. Put shared commands and repository rules at the root, move domain detail into imports or path rules, and reserve skills for routines that load on demand. Check `/context` when a session drifts, then move must-hold boundaries out of prose and into enforcement. ## FAQ ### Where should I put CLAUDE.md? Developers should put shared Claude Code project instructions in CLAUDE.md or .claude/CLAUDE.md at the repository root and commit them. Put personal rules that apply to every project in ~/.claude/CLAUDE.md. Use CLAUDE.local.md for private project instructions and keep it out of Git. Managed organization instructions belong in the policy location for the operating system. ### How long should CLAUDE.md be? Anthropic recommends fewer than 200 lines per CLAUDE.md file; this is a target, not a hard limit. Keep only instructions Claude cannot infer by reading the repository, such as uncommon test commands, branch rules, architecture decisions, and known traps. CLAUDE.md loads in full each session, so remove prose that does not prevent a likely mistake and link to focused detail instead. ### Does CLAUDE.md enforce rules? CLAUDE.md does not enforce rules. Claude Code reads CLAUDE.md as project instructions, but vague, conflicting, or buried rules can be missed. Use a PreToolUse hook, permission rule, sandbox, branch rule, or CI check when a boundary must hold. Keep CLAUDE.md for guidance that helps the model choose the right code pattern and workflow. ### What is the difference between CLAUDE.md and auto memory? CLAUDE.md is authored project or personal instruction text that you review and may share through Git. Auto memory is machine-local information Claude records while you work. Its MEMORY.md index is partly loaded at session start, while topic files are read when needed. Use /memory to inspect, edit, disable, or remove saved entries. --- ## Claude Code: MCP & Hooks URL: /ai-coding-tools/claude-code/mcp-and-hooks/ Cluster: AI Coding Tools > Claude Code Claude Code already reads files, edits code, and runs commands. MCP servers give it named tools for services outside that built-in set, while hooks run checks at fixed points in a session. They solve different problems, but both can widen what Claude is allowed to do. Add one MCP server or one blocking hook at a time, then test it before relying on the new access. I rely on hooks when a repeated instruction needs enforcement, because a visible deny result tells me more than another reminder in a prompt. MCP and hooks extend an agent you have already chosen; our [guide to AI coding assistants](/ai-coding-tools/) helps with the earlier decision about which tool to use. Keep shared project instructions in the setup described by the [Claude Code configuration and context guide](/ai-coding-tools/claude-code/configuration-and-context/), rather than repeating them across every hook. ## What Do MCP Servers Add to Claude Code? MCP servers add tools that Claude Code can call to work with another service or local process. MCP stands for Model Context Protocol, the open protocol the client and server use to describe and invoke those tools. The [main Claude Code guide](/ai-coding-tools/claude-code/) covers installation and the base agent loop before you extend it with another process or service. One server might offer issue-search and ticket-update tools. Another server might run read-only database queries. Claude sees each tool's name, description, and input fields, then can call it during the normal agent loop. The server can run as a local process, connect over HTTP, or come from a plugin. A local server gets the same machine access as the command that starts it. A remote server sends requests to another service. Read the server's own setup page before adding it because the transport tells you where code, prompts, and credentials may travel. ### Add an MCP Server Use `claude mcp add` for either a remote HTTP endpoint or a local process. The command needs a short name that you will recognize in `/mcp`. Most setups use one of these two command shapes: ```bash # Remote HTTP server claude mcp add --transport http issue-tracker https://mcp.example.com/mcp # Local stdio server claude mcp add local-tools -- /absolute/path/to/server ``` Run `claude mcp list` to confirm the server is registered, then open Claude Code and run `/mcp` to inspect its connection and tools. Begin by asking one read-only question through the server. For an issue tracker, that could be "list the open tickets assigned to me." Do not begin with an update, delete, or send action while you are still checking authentication and scope. The [official MCP guide](https://code.claude.com/docs/en/mcp) also shows header-based authentication and variable expansion. Keep the secret outside a committed `.mcp.json` file. Put only the variable name in shared settings, then define the secret on each developer's machine or in the approved secret store for the runtime. ### Pick the Right MCP Scope | Scope | Stored For | Use It When | | Local | One project | Private trial | | Project | `.mcp.json` | Shared server | | User | All projects | Personal default | Local is the default and the safest place to test. Project scope writes shareable settings, so review the command, URL, headers, and variable names before committing it. User scope is convenient for a tool you need in many repos, but it also makes that tool available in sessions that may not need it. ## Control MCP Access and Risk An MCP server is executable software plus a set of model-visible tool descriptions. Check who publishes it, where its source lives, what account permissions it requests, and how updates arrive. A familiar service name does not prove that an unrelated package is maintained by that service. Tool descriptions can steer the model, which creates a tool-poisoning risk if a server is hostile or becomes hostile after an update. Give the server a token limited to the smallest useful job. A ticket reader does not need ticket-delete rights. A database inspector should begin on a read-only account, never the production owner account. Claude Code defers most MCP tool definitions and loads them through Tool Search when needed. That cuts the context cost, but a local server may still keep a process running. Use `/mcp` to inspect each server's tool count and context cost. Remove a server when its occasional convenience no longer pays for its access, process, and update path. ## Troubleshoot an MCP Connection Start with `claude mcp list`, then inspect the server in `/mcp`. "Disconnected" means Claude cannot reach the process or endpoint. "Needs authentication" means the transport works but the server still needs a login or token. A connected server with no useful tools points back to the server's own configuration. - For a local server, run its command outside Claude Code and read the first error. - Use absolute executable paths when the process works in your shell but not in the app. - Confirm shared settings refer to a variable name that exists on this machine. - For HTTP, check the endpoint, transport, headers, and whether the service still accepts that route. - Restart the session after editing settings so the tool list is discovered again. Turn on Claude Code debug logging when the server exits without a clear message. Do not paste a debug log into an issue until you have removed tokens, cookies, request headers, and private tool output. Authentication logs can expose the secret you were trying to fix. ## When Should You Use Claude Code Hooks? Developers should use Claude Code hooks when a session event must trigger a check, a block, or another repeatable action. `PreToolUse` fires before a tool call and can block it. `PostToolUse` fires after a successful call and can add feedback, but it cannot undo work that already happened. `Stop` fires when Claude is about to finish, which makes it useful for a final check. Configure hooks through `/hooks` or in a settings file. The user-level hook file is `~/.claude/settings.json` on your machine. Shared project hooks are stored in `.claude/settings.json` inside the repository. Private project hooks belong in `.claude/settings.local.json`, which should stay out of Git. | Hook | Event + matcher | What it does | | Auto-format | `PostToolUse`, `Edit|Write` | Formats each edit | | Block commands | `PreToolUse`, `Bash` | Denies matched commands | | Test at stop | `Stop`, no matcher | Gates completion | ### Build a Blocking Hook This configuration expects an executable script at `.claude/hooks/block-rm.sh`; create and test that script before enabling the hook. The `if` condition matches parsed `rm` commands, including commands inside a shell chain, rather than checking only the beginning of the full command string: ```json { "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "if": "Bash(rm *)", "command": "\\"${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh\\"" } ] } ] } } ``` The script reads the tool input as JSON from standard input. If it finds a forbidden command, it writes a plain reason to standard error and exits with code 2. Claude Code blocks the tool call and feeds the reason back to Claude. An exit code of 0 lets normal permission processing continue; it does not grant unconditional approval. Do not treat a string search for `rm -rf` as a complete command policy. Shell text can use aliases, scripts, variables, or a different destructive command. Keep the first hook narrow and explicit. When the rule needs command parsing, write tests for the parser or move the control to an operating-system sandbox. ### Test Hooks Before Trusting Them - Run `/hooks` and confirm the hook appears at the intended scope. - Call the script with a saved sample JSON payload before asking Claude to trigger it. - Try one allowed command and one harmless command shaped to hit the deny branch. - Use `claude --debug` when the hook does not fire or its JSON cannot be parsed. - Keep standard output clean when returning JSON; stray shell-profile text breaks parsing. Several hook events cannot block because they run after the relevant action or mark a lifecycle boundary. The [hook reference](https://code.claude.com/docs/en/hooks) lists the behavior for each event. Read that row before assuming exit code 2 will stop anything. ## Choose MCP, a Hook, or CLAUDE.md | Need | Use | Example | | Reach another service | MCP | Read one issue from a tracker | | Enforce an event rule | Hook | Block edits to generated files | | Tell Claude a convention | CLAUDE.md | Use pnpm for this repo | CLAUDE.md is advice loaded into the model's context. It works well for a style rule, a test command, or a pointer to a code pattern. It cannot guarantee that Claude will follow the text. A hook is the better fit when the action must be checked every time. Use MCP only when Claude needs a new tool; project instructions and enforcement rules have their own jobs. These three layers can work well together in one project. A project may expose an issue tracker through MCP, explain the ticket routine in CLAUDE.md, and use a `PreToolUse` hook to deny issue deletion. Each layer then owns one clear job, which makes failures easier to trace. ## Choose the Smallest Extension Add one MCP server when Claude Code needs a service it cannot reach through its built-in tools. Add one hook when a named session event must trigger a check or block. Keep both at the smallest scope that works, test their failure paths, and remove them when the access they add no longer earns its place. Continue with [Workflows, Debugging & Safety](/ai-coding-tools/claude-code/workflows-and-safety/) for session planning and permission modes. ## FAQ ### What is the difference between MCP and hooks? MCP gives Claude Code a tool for reaching another service or local process. A hook runs when a Claude Code event occurs, such as before a shell command or after a file edit. Use MCP when Claude needs another capability. Use a hook to check, block, log, or respond to an action. ### Where does Claude Code store MCP settings? Claude Code stores local-scope MCP servers in your user configuration for one project. Project-scope servers are written to .mcp.json so the team can share them through Git. User-scope servers become available across all your projects. Run claude mcp list and inspect .mcp.json before sharing a setup that contains URLs, commands, or variable names. ### Can a hook stop a command before it runs? A Claude Code PreToolUse hook can inspect a proposed tool call and deny it before Claude Code reaches the normal permission check. A command hook can exit with code 2 and write the reason to standard error, or return a structured deny decision. PostToolUse runs too late to undo the action. ### Should I install every MCP server I use once? Claude Code users should install MCP servers for recurring tasks rather than every service they use once. Start with one server tied to a frequent task, then check its tool list and context cost with /mcp. Each local server can add a running process, credentials, and another update path. Remove servers that do not save enough work to justify their access and upkeep. --- ## Claude Code: Workflows, Debugging & Safety URL: /ai-coding-tools/claude-code/workflows-and-safety/ Cluster: AI Coding Tools > Claude Code A reliable Claude Code workflow has four steps: inspect the relevant code, agree on a small change, implement it with appropriate permissions, and verify the result. Give each task a clear scope and a check that can show whether it worked. I use Claude Code as the control room for agent work, and the biggest improvement came from enforcing repeated boundaries with hooks instead of explaining the same rule again. These workflows assume you have chosen Claude Code. If you are still deciding, the [AI coding tools comparison and recommendations](/ai-coding-tools/) explain where it fits alongside other options. The [configuration and context guide](/ai-coding-tools/claude-code/configuration-and-context/) explains where to keep the project instructions that support this review process. ## Start with a Verifiable Task The [main Claude Code guide](/ai-coding-tools/claude-code/) covers installation before this scoped workflow begins. Give Claude one outcome, the scope it may touch, and the check it must run. "Fix login" gives it too many paths. A better request names the symptom, likely code area, constraint, and proof: > The session expires after token refresh in `src/auth/`. Reproduce it with a focused test, fix the cause without changing the public session type, run that test, and show the result. The test matters more than extra prompt polish. Without a pass-or-fail signal, Claude stops when the work looks complete. With one, it can run the check, read the failure, revise the patch, and repeat. Anthropic's [best-practices guide](https://code.claude.com/docs/en/best-practices) puts verification first for this reason. Choose proof that matches the work being changed: - A function fix needs focused tests, then the broader suite if shared code moved. - A build setup edit needs the production build command and its exit code. - An API task needs a request against a test endpoint with the expected status and body. - A page layout needs the build plus screenshots at the required phone, tablet, and desktop sizes. ## Explore, Plan, Implement, and Verify Plan mode lets Claude read files and run inspection commands without editing source files. Enter it with `Shift+Tab`, prefix one prompt with `/plan`, or start the CLI with `claude --permission-mode plan`. Use it when you do not yet know which files or data paths the task crosses. 1. Explore: ask Claude to trace the current path and name the files involved. 2. Plan: ask for the smallest patch, expected risks, and verification commands. 3. Implement: approve the plan in the permission mode that fits the task. 4. Verify: have Claude run the agreed checks and show their output. Read the plan as a scope contract. A plan that says "refactor the auth layer" when you asked for one refresh bug needs another pass. Press `Ctrl+G` to open the proposed plan in an editor, or give feedback and keep planning. Skip this overhead for a typo, a single log line, or another edit whose diff is obvious before you begin. Keep each work slice small enough to review. A request that mixes a bug fix, a settings redesign, and a loading state has three success tests and three ways to fail. Split the request before Claude starts editing. Each slice gets its own diff, proof, and commit, which makes review and rollback much less costly. ## Debug with Evidence Give Claude the full error and keep your first guess separate. Paste the stack trace, name the command that produced it, and mention whether it fails every time. Use `@` references for the nearest files so Claude can see imports and call sites instead of an isolated line. Ask for a reproduction before a fix when the bug is not obvious. A failing test pins down the symptom and prevents a patch that only hides the error. For state bugs, ask Claude to trace where the bad state begins and list each handoff. For a build error, ask it to read the first root error rather than suppressing every later message. Stop the loop if Claude proposes the same idea twice. Check the base facts yourself: the database is running, the required shell variables exist, the failing request reaches the server, and the branch contains the code you think it does. Then start a cleaner prompt with the new evidence. More turns do not rescue a false premise. ## Manage Session Context A session holds the conversation, file contents, command output, CLAUDE.md, auto memory, loaded skills, and system instructions. That shared context fills while Claude investigates the repository. Run `/context` to see where the space went instead of guessing. Run `/clear` whenever you switch to an unrelated task. Use `/compact focus on the auth changes` when you must keep the session but want a shorter history. Claude also compacts on its own near the limit, first clearing old tool output and then summarizing the conversation. Early detail can be lost, so persistent project rules belong in CLAUDE.md rather than a message sent hours ago. A clean restart is often cheaper than correcting a session filled with rejected plans. Write down the chosen approach, current file state, and next check, then start a fresh session. Use a subagent for broad investigation when you want its file reads kept out of the main context. ## Use Permission Modes Deliberately Choose a mode by the actions the task needs, then check the active mode before work starts. Terminal and VS Code defaults can differ from Desktop; account settings and organization policy can change availability. | Mode | What Claude Can Do | Best Fit | | Manual | Asks before edits and commands | New repo or sensitive task | | Accept Edits | Edits files without each prompt | Scoped refactor with diff review | | Plan | Inspects and proposes, no source edits | Research and scope agreement | | Auto | Uses background safety checks | Trusted direction with fewer prompts | You can cycle through these modes with `Shift+Tab`. The [permission-mode guide](https://code.claude.com/docs/en/permission-modes) also documents Bypass Permissions mode. That mode removes the normal approval layer and should not be the default on a personal machine. Do not solve approval fatigue by allowing every shell command. Add narrow allow rules for commands whose effect you know, such as a focused test or `git status`. Keep deploy, database, account, secret, and destructive commands behind a prompt even when file edits are accepted. ## Pair Permissions with Hard Boundaries Permission settings decide when Claude must ask you first. They are not the same as an operating-system sandbox. If a process has broad file and network reach, one approved command may still do more than you expected. Use `/sandbox` or an isolated runtime when unattended work needs wider freedom. Limit every credential to the exact task at hand. A test deployment should not inherit production database keys. A code-reading job does not need write access to a Git host. Narrow file roots and network domains reduce the cost of a mistaken command or hostile text pulled from a webpage, issue, or dependency. Rules written in CLAUDE.md remain advisory model instructions. They tell the model how the project works, but the model can miss a vague or buried line. Use a `PreToolUse` hook for a rule that must block an action, such as writes to generated files. The [MCP and hooks guide](/ai-coding-tools/claude-code/mcp-and-hooks/) shows the blocking pattern. ## Recover with Checkpoints and Git Claude Code snapshots changes made through its direct file-editing tools. Press Escape twice or run `/rewind` to open the checkpoint menu, then restore the conversation, the code, or both. This is quick when the last prompt sent the patch in the wrong direction. Checkpoints do not track file changes made through Bash commands, including moves or deletions. Emails, payments, database updates, and deployments also sit outside that recovery boundary, so those commands need tighter permissions and purpose-built rollback steps. Begin each task by checking the current Git status with `git status`. Commit or set aside your own work before handing the repo to an agent. After implementation, inspect `git diff --stat`, read the full diff, and run the checks yourself when the risk calls for an independent run. A checkpoint helps inside the session; Git records the project history you can return to later. ## Know When to Stop the Agent - Stop when the same failed fix comes back after you rejected it. - Stop when the proposed scope spreads beyond the files tied to the request. - Stop when a command touches production, billing, accounts, or secrets without an agreed step. - Stop when a visual task has no rendered check or a behavior task has only a screenshot. - Stop when you cannot explain the diff well enough to review it. Some jobs fit another surface better. A tiny inline edit may be faster in an editor such as Cursor, the top-ranked editor in our [list of AI coding tools](/ai-coding-tools/). A layout needs a rendered browser with interaction evidence. A new hosted prototype may be easier in a tool that supplies its own runtime. Hand the agent work that benefits from repository search, command execution, and repeatable checks. ## The Boundary That Matters A sound Claude Code workflow is small enough to review and strict enough to prove. Scope one outcome, plan when the path is unclear, require a matching check, and grant only the access that step needs. Keep checkpoints for quick recovery and Git for durable rollback. When the evidence or scope stops making sense, stop the agent and restart from facts. ## FAQ ### When should I use Plan mode? Developers should use Claude Code’s Plan mode when the task spans several files, the route through the code is unclear, or you need to agree on scope before edits begin. Skip it for a tiny fix whose diff you can describe in one sentence. Plan mode can read and inspect, but it does not edit source files. ### What proof should I ask Claude Code to produce? Claude Code should provide evidence that matches the requested result: a focused test, a production build, a type check, a screenshot, or a response from a test endpoint. Have Claude show the command and outcome. A green build does not prove a visual task, and a screenshot does not prove hidden behavior. ### Does a checkpoint replace Git? Claude Code checkpoints do not replace Git. A checkpoint can restore changes made through Claude Code’s direct file-editing tools during a session. It does not track file changes made through Bash commands. It cannot undo a database write, deployment, API call, or other remote side effect. Git gives you a durable project history, so begin from a clean status and review the diff before committing. ### Is Bypass Permissions mode safe for local work? Claude Code’s Bypass Permissions mode is a poor default for local work because it removes the normal action-approval boundary. Use Manual, Plan, Accept Edits, or Auto mode based on the task, then allow only commands you trust. If unattended work needs broader access, put it inside an isolated runtime with narrow files, credentials, and network reach. --- ## Getting Started with the Codex Desktop App URL: /ai-coding-tools/codex/codex-desktop/ Cluster: AI Coding Tools > OpenAI Codex Codex is the coding workspace in the ChatGPT desktop app. It gives you a visual place to hand an AI coding agent a project and a task. You can watch it read files, run commands, and propose edits without beginning in a terminal. I use Codex as a second coding agent, and the desktop view is the easiest place to see what each task is doing. Your first hour should end with one small, reviewed task inside a folder you can restore. If you are choosing your first coding assistant, compare Codex with the other [AI coding tools](/ai-coding-tools/) before committing to a desktop workflow. ## What Is the Codex Desktop App? Codex is a dedicated coding view inside the ChatGPT desktop app. OpenAI's current app places Chat and Work under ChatGPT, with Codex beside them in the main switcher. Codex keeps its coding chats and history separate, and it can work with local folders, repositories, terminals, and developer tools. If you used the earlier standalone Codex app, updating it moves you into the new ChatGPT desktop app. Your existing Codex chats and projects should remain. A first-time user downloads the same ChatGPT app for macOS, Windows, or Linux, signs in, and selects Codex from the top-left menu. The desktop interface lets you start before you memorize terminal commands. You still need to understand the result. Codex acts on files and may run shell commands, so a readable plan, narrow access, and a clean review matter from the first task. ## Create Your Account This walkthrough uses your ChatGPT account, so you do not need an API key. Codex also supports API-key authentication for local work, with separate billing and feature availability; cloud tasks require ChatGPT sign-in. 1. Open [chatgpt.com](https://chatgpt.com) in a browser. 2. Select Sign up and finish the account flow. 3. Use the same account when the desktop app asks you to sign in. 4. If this is a work account, choose the workspace that has Codex access. A Business, Enterprise, or Edu workspace can control Codex through permissions and roles. If Codex is visible but unavailable, ask the workspace owner to check Codex Local access. Creating another personal account will not fix a policy set by the work workspace. ## Pick a Plan Start with the ChatGPT plan you already have. OpenAI changes included Codex usage, model access, and extra-credit rules, so the live [Codex pricing page](https://learn.chatgpt.com/docs/pricing) is the source to check before paying. Free and Go provide limited desktop access. Start there if it covers your task; compare paid plans only when the available models or allowance become a practical limit. On a new account, try the included access and check the usage shown in the app. If you reach a limit, inspect the task usage before comparing plan allowances or extra credits. Work accounts also need the administrator to confirm workspace access. Several parallel agents should each have a named task so shared usage remains explainable at review time. Give extra credits a named owner and a spending alert before parallel work begins. A larger allowance does not make an unclear task better. Run one job you can check yourself, then look at its usage and result. Upgrade only when the current limit blocks work that has already proved useful. ## Install and Open Codex 1. Download the [ChatGPT desktop app](https://chatgpt.com/download) for macOS, Windows, or Linux. 2. Open the installer and follow the operating system prompts. 3. Launch ChatGPT and sign in with the account you set up. 4. Select Codex from the switcher in the top-left corner. 5. Select New chat when the Codex workspace opens. You may still see ChatGPT Classic if the new app installed beside an older copy. OpenAI says Classic will continue to receive fixes, but new agent features may appear only in the new app. Use the app whose switcher shows ChatGPT and Codex if you want to follow this guide. The operating system may ask whether the app can reach the selected folder or run a local command. Read the request against the task in front of you. Access to one tutorial repository is a useful first grant; access to a home directory, cloud-sync root, or folder containing several clients is not. Broader access can wait until a real task needs it. If the Codex switcher is missing, check the simple causes in order. Confirm that you opened the new ChatGPT app, install the current update, and verify the signed-in account and workspace. Then ask a workspace owner whether Codex Local is enabled. Reinstalling the same app will not override an organization policy, and opening a larger folder will not reveal a feature the account cannot use. ## Set Up Your First Project Choose a folder you can afford to inspect and replace. A small tutorial repository, a new branch, or an empty folder is better than the production checkout that holds today's release. The folder sets the starting boundary for what Codex can read and change. If the folder is a Git repository, begin with a clean status. Git then shows every tracked change Codex makes. Keep unrelated personal files outside the project, and do not place credentials in a file just because the first task might need them. - Good first project: one small app with a working test command. - Good first branch: a short-lived branch with no unrelated edits. - Poor first project: a monorepo you do not yet know how to build. - Poor first boundary: your home folder or a directory full of client projects. ## Give Codex Its First Task Give the first task four parts: the outcome, the files or area in scope, the check to run, and anything it must not change. This is more useful than a long role prompt. It tells Codex what a finished result looks like and gives you the same checklist for review. First Task Example Fix the failing email validation test in `src/account/`. Change only the validator and its tests. Run the focused account test command. Do not add packages or change the public error message. Show me the diff and test result when finished. Read Codex's proposed approach before you approve an action. If it names unrelated directories or wants to install a package for a small fix, stop and narrow the task. A mistaken plan is cheaper to correct than a large diff built on the wrong assumption. ## Review Before You Keep the Work Review starts with the changed-file list. Every file should connect to the task you gave. Then read the diff, inspect any command Codex ran, and check the complete test output. A green summary is not enough if the agent changed the test so the bug could no longer fail. Run the required project check yourself after the task stops. For a web change, open the result in the project's approved visible browser flow. For a data migration, inspect the generated SQL or migration file before it reaches a shared database. - Confirm the requested behavior changed. - Confirm unrelated behavior did not change. - Check for new packages, scripts, and configuration. - Read warnings as well as the final exit code. - Commit only after the diff matches the task. ## Use Parallel Projects Carefully The app keeps several projects and long-running chats in one workspace. Codex also supports Git worktrees, which place an isolated branch in another folder so one task does not overwrite another task's files. OpenAI's worktree guide warns that each worktree is still part of the same repository. Parallel work multiplies review. Two agents can finish at the same time, but you still need to understand two diffs and any overlap between them. Learn the single-task loop first. Add another task only when each branch has a separate purpose, owner, and test. The [Codex mobile guide](/ai-coding-tools/codex/codex-mobile/) covers remote steering when a task keeps running away from your desk. The [main Codex guide](/ai-coding-tools/codex/) covers the CLI, code review, models, and the broader choice between Codex and other coding agents. ## A Practical First Project The Codex desktop app is the right starting point when you want an agent to work on a local project but do not want your first step to be a command-line install. Its project list, visible actions, diffs, and test output make the work easier to follow than a stream of terminal text. Begin with one small repository and one task you already know how to check. Once that review loop feels routine, add the CLI, Remote, skills, or parallel work for a specific need rather than switching on every feature at once. ## FAQ ### Is Codex a separate desktop app? Codex is now a dedicated coding view inside the ChatGPT desktop app on macOS, Windows, and Linux. It keeps its own projects, chats, and coding workflow, even though Chat, Work, and Codex share one installed app. Existing Codex app users can update into the new ChatGPT desktop app. ### Do I need to know terminal commands? Codex desktop does not require terminal knowledge to get started. You can open a folder, describe a task, approve actions, inspect files, and review changes through the desktop interface. Terminal knowledge becomes useful when you want to understand commands or move into the Codex CLI, but it is not required for your first desktop task. ### Can Codex work on more than one task? Codex can work on more than one task. The desktop app keeps chats and projects in one workspace, and Codex can isolate work in Git worktrees when you choose that environment. Parallel tasks still create parallel review work, so begin with one task until you can check its files, commands, and test results with confidence. ### Does Codex keep every change it makes? Codex changes can remain in your working files until you undo or restore them; you decide which changes to commit or merge. Codex shows its work for review, and Git gives you a record of file changes when the project is a repository. Start with a clean branch or test folder, inspect the full diff, and run the project's checks before you commit or merge anything. --- ## Using OpenAI Codex from the ChatGPT Mobile App URL: /ai-coding-tools/codex/codex-mobile/ Cluster: AI Coding Tools > OpenAI Codex Codex on mobile lets you continue coding tasks away from your desk. In the ChatGPT iOS app, open Codex to connect to a Mac or Windows host, or select a published cloud environment for cloud work. Older app versions may label the host connection Remote. This guide covers the connected-computer workflow: the phone sends instructions and displays results while the host supplies project files and development tools. I trust Codex as a second agent, and on a phone I limit that trust to steering and review decisions that fit the screen. Phone access is one part of the decision; our [coding assistant recommendations](/ai-coding-tools/) compare the underlying tools you will be supervising. The [Codex overview](/ai-coding-tools/codex/) explains the product and review workflow behind these remote sessions. ## How Does Codex Remote Access Work? Codex Remote gives you access to coding work running on a connected computer. The mobile app can start a new chat in a host project or continue an existing one. It can send follow-up instructions, answer Codex's questions, approve actions, read outputs, and move between connected hosts. For connected-computer work on iOS, open Codex in ChatGPT, or Remote if your app still uses that label. It opens supported chats running on a connected computer. Those remote chats remain separate from ordinary mobile or web chat history. The host supplies the files, terminal, credentials, plugins, browser setup, permissions, and other local tools. The phone displays that session and sends your choices back. This split explains why the computer must remain awake and why you should secure both devices. ## Check the Host and Account Set up the desktop side first. Install the latest ChatGPT desktop app on a Mac or Windows computer, sign in, open Codex, and confirm that a small local project works. If you cannot complete a normal desktop task, pairing a phone adds another place to troubleshoot without fixing the base problem. - Use the latest ChatGPT desktop app on the host. - Use the latest ChatGPT iOS app for the host-connection workflow documented here. - Sign into the same account and workspace on both devices. - Finish any required passkey, multi-factor, or single sign-on step. - Ask the workspace owner whether Remote Control is allowed on managed accounts. Host access requires the same account and workspace on both devices, an online host, and any required workspace permission. Cloud tasks use a separate environment and do not depend on keeping your personal computer awake. ## Pair Your Phone and Host 1. On the host, open the ChatGPT desktop app and open Settings > Connections > Control this Mac or PC , then choose Set up or Add . 2. Enable remote access for that computer when the app asks. 3. Scan the displayed QR code with the phone you want to pair. 4. Finish the connection in ChatGPT with the same account and workspace. 5. Complete any extra sign-in check required by the workspace. 6. Open Codex (or the older Remote entry) on the phone and confirm that the host appears. The QR code pairs one controller with one host. Repeat the flow for another phone or computer. OpenAI also says older connections that had not been used since June 8, 2026 may need to be paired again after both apps are updated. In the host app, open Settings > Connections after pairing. Check the device list, decide whether the computer should stay awake while plugged in, and leave Computer Use or the Chrome extension off unless the task needs them. ## Start or Resume a Task Open Codex on the phone and choose the paired host. You can continue a running chat or start a new chat inside one of that host's projects. The new task uses the chosen computer's project folder and local access. The phone stores neither the files nor the working copy. A mobile prompt should be easier to verify than to type. Name the task, path, stopping point, and check. If the job needs a long spec, write it in the repository or begin at the desk. A few thumb-typed lines are a poor place to define a migration, permission policy, or product requirement. Mobile Task Example Continue the failing account test from this morning. Stay in `src/account/`. Show me the proposed file list before editing, run the focused test, and stop after the first passing diff. The host list matters when you use more than one computer. Confirm the machine and project before you send the prompt. A task aimed at a personal laptop may have different files, credentials, and branch state from a similar project on an office workstation. ## Steer and Approve from Mobile Codex can pause to ask a question or request approval. Remote surfaces that prompt on the phone. Read the full action, the project, and any shown command before choosing. Mobile access changes where you approve the action; it does not turn a risky command into a routine one. | Prompt | Phone Fit | Response | | Run focused tests | Usually good | Check command | | Read another file | Often good | Check path | | Add a package | Poor | Wait for desk | | Change auth or data | Poor | Review in full | Use steering messages to correct direction before more files change. "Stop editing and show the current diff" is a valid response. So is "return to the original file list." If the reason for a command does not fit on the screen or the change has wider effects, leave the task paused. ## Review Progress Without Merging Remote can show terminal output, diffs, tests, screenshots, project context, and plugin results from the host. This is enough to answer a progress question: Is the test still failing? Did Codex touch the wrong folder? Does the screenshot show the requested state? Final review still belongs on a larger screen. A phone diff can hide the relation between several files, and a screenshot cannot prove keyboard use, error handling, or network behavior. Use mobile review to continue, pause, or redirect the task. - Continue: the file list and focused check match the task. - Pause: the diff is too large to read without context. - Redirect: the agent moved beyond the named scope. - Reject: the action can delete data or widen access. Save the merge decision for a full screen. Read every changed file, rerun the repository's required checks, and use the approved browser or device flow for user-interface work. Mobile convenience can shorten idle time while the release gate stays unchanged. A completion notification prompts you to inspect the result. Confirm that the final command passed, the diff stayed in scope, and another task did not change the same checkout. ## Manage Connections and Security Remote reaches the host's projects and credentials through your signed-in account, so account security matters. Use the workspace's required authentication and lock the phone. On a shared or managed device, do not leave a remote session open where another person can approve actions. Review connections in the desktop app after replacing a phone, leaving a team, or retiring a computer. Remove devices you no longer use. Turn off remote access when the host should not accept controllers, and close the desktop app to disconnect remote access. This does not mean every cloud task is canceled; stop a task explicitly in its own controls when cancellation is the goal. Remote keeps the source tree on the host. The phone can still display code, terminal text, screenshots, and diffs. Treat what appears on screen as project data. Avoid opening a client repository over a public display or while screen sharing with the wrong audience. ## Know What Needs a Desk The phone cannot replace a working development setup. The host must provide the repository, tools, and runtime. Remote also stops when the host sleeps, loses its connection, signs out, or closes the desktop app. Keep an always-on host only when the access and power trade-off is deliberate. Use the desk for the parts that set authority or decide what ships: changing permissions, adding plugins, reviewing large diffs, resolving merge conflicts, inspecting browser behavior, approving migrations, and merging a pull request. The [Codex desktop guide](/ai-coding-tools/codex/codex-desktop/) covers the first local task and review loop. ## What Belongs on the Phone Codex Remote fits the waiting and steering parts of agent work: checking progress, answering a narrow question, approving a known test command, or stopping a task that has drifted. It saves a trip back to the desk without moving the repository or development tools onto the phone. Keep final code review and release decisions on a full screen. With that boundary, Remote extends a desktop Codex workflow while the full review evidence stays at the desk. ## FAQ ### Is Codex a full mobile coding app? Codex on mobile controls work on a connected computer or cloud environment rather than providing a full local development environment on your phone. For host-backed work, the phone controls the session while the computer retains the files and tools. ### Can Windows host a Codex Remote session? Windows can host a Codex Remote session. OpenAI's Remote documentation lists the ChatGPT desktop app on macOS and Windows as supported hosts. Availability can vary during rollout, and Enterprise workspaces may need an administrator to enable Remote Control before a phone can connect to a Windows or Mac host. ### Does the host have to stay awake? A computer hosting a Codex Remote session must stay awake, online, signed into the same account and workspace, and running the ChatGPT desktop app. Remote access stops if that computer sleeps, loses its network connection, or closes the app. The host settings may offer a keep-awake option while it is plugged in. ### Can I pair more than one host? Codex Remote supports pairing with multiple hosts and can switch between connected hosts and chats. Pair each phone or supported desktop controller with each host you want it to control. Name and review those connections in the desktop app, then remove old devices so the list still reflects the computers and phones you use. --- ## Best Open Source Models for Coding (2026) URL: /ai-coding-tools/comparisons/best-open-source-models/ Cluster: AI Coding Tools > Comparisons For local coding on a 64GB Mac, Qwen3.6-27B remains my starting recommendation. For demanding coding tasks through a hosted service or your own server infrastructure, I would compare DeepSeek V4 Pro, GLM-5.2, and Kimi K3 on the same repository. The right choice depends on your hardware, coding tasks, license requirements, and whether your code can leave your machine. A model still needs a client and a review workflow. The [AI coding tools guide](/ai-coding-tools/) helps you choose the application around it. The [OpenCode and Ollama setup guide](/ai-coding-tools/opencode/local-models/) turns that model shortlist into a local coding workflow you can test. ## Choosing by Use Case Choose a model for your hardware and coding tasks. A newer release is a candidate to compare, not proof of better results. | Best Fit | Model to Evaluate | Deployment and Version Note | | Local coding on a 64GB Mac | Qwen3.6-27B; compare Qwen3.8-27B | Original recommendation plus a newer candidate; choose a supported local quantization. | | Demanding hosted coding tasks | DeepSeek V4 Pro | Evaluate through a hosted provider; self-hosting needs substantial infrastructure. | | Long-running agent tasks | GLM-5.2; compare GLM-5.3 | 5.2 is not the latest release; evaluate 5.3 for a new deployment. | | Server-scale agentic coding | Kimi K3 | Hosted trial or substantial server deployment; review the K3-specific license. | | Long-context experiments | Llama 4 Scout | Large memory requirement; the context ceiling does not prove coding quality. | | Local deployment with Apache-2.0 weights | gpt-oss-20b | Smaller deployment option; check runtime support and memory overhead. | | Research and reproducibility | OLMo 3 Instruct, 7B or 32B | Choose the checkpoint for available hardware; prioritize access to training materials. | ## What is the difference between open-source and open-weight models? Open-weight models make their trained parameters available to download; open-source AI models also provide the code, data information, and usage rights needed to study, modify, use, and share the system. Downloadable weights alone do not establish that a model is open source or unrestricted for commercial use. Most models in this shortlist are more precisely described as open weight. Check the exact release’s license and published training resources. Our [open-source versus proprietary guide](/ai-coding-tools/comparisons/open-source-vs-proprietary/) explains how those differences affect deployment choices. ## How We Selected These Models I choose models by their coding capabilities, downloadable weights and license terms, hardware requirements, and the tools needed to run them. My preferences are separate from specifications and vendor-reported results. Qwen3.6-27B remains my starting recommendation for a 64GB Mac. I have not run a fresh controlled test of every model, including the newer releases. For a new GLM deployment, compare GLM-5.3 with 5.2. Benchmarks with different test conditions cannot establish a universal ranking. ## DeepSeek V4 Pro DeepSeek V4 Pro belongs on a shortlist for demanding coding tasks. Its published coding results support evaluating it, but do not establish a universal lead on your repository. Best for: Developers evaluating a large model for complex coding and reasoning work through hosted inference or a substantial server deployment. Main limitation: The full model has 1.6 trillion parameters. Its 49 billion active parameters describe computation per token, not total storage or memory needs. The published 1M-token context is a capacity ceiling, not a guarantee of reliable repository understanding. Deployment: Start with hosted access unless you already operate suitable infrastructure. Check the exact revision, provider rates, and data policy. Compare a current Flash variant when cost and throughput matter more than the largest model. Source: [DeepSeek V4 Pro model card and deployment guidance](https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro). ## GLM-5.2 GLM-5.2 is a candidate for long-running coding agents that plan, call tools, and check their work. Its published 1M-token context supports evaluating those workflows. Best for: Teams comparing models for multi-step repository work through hosted inference or managed server infrastructure. Main limitation: GLM-5.2 is a large model with 753 billion total parameters, and it is no longer the newest GLM release. Neither a large context window nor vendor results guarantee reliable completion of your tasks. Deployment: Retain 5.2 in an existing workflow if its results justify it. For a new deployment, compare 5.3 on the same tasks before choosing. I am not reporting a side-by-side test of both releases. Sources: [GLM-5.2 model card](https://huggingface.co/zai-org/GLM-5.2) and [newer GLM-5.3 model card](https://huggingface.co/zai-org/GLM-5.3-BF16). ## Qwen for Local Coding Qwen is my starting point for local coding when the model fits the available memory. My original recommendation was Qwen3.6-27B on a 64GB Mac. Qwen3.8-27B is a newer candidate to compare; I have not personally retested it. Best for: Developers evaluating downloadable coding models on a local workstation rather than operating a large inference cluster. Main limitation: A 27B parameter count does not establish a fixed memory requirement or usable speed. Quantization, context, runtime, and other applications all affect the result. Deployment: Choose a supported local build and leave memory for the editor, browser, and build tools. Keep the model version and quantization fixed when comparing tasks. A hosted Qwen product name does not by itself prove that the same checkpoint is downloadable. Sources: [Qwen3.6-27B model card](https://huggingface.co/Qwen/Qwen3.6-27B) and [Qwen3.8-27B model card](https://huggingface.co/Qwen/Qwen3.8-27B). ## Kimi K3 Kimi K3 is a candidate for long coding sessions that require repository navigation and tool use. Moonshot publishes its weights and vendor evaluations, so deployment choices can be assessed against an identifiable release. Best for: Teams evaluating server-scale agentic coding through a hosted service or substantial inference infrastructure. Main limitation: K3 has 2.8 trillion total parameters and uses its own license terms. It is not a laptop recommendation, and older K2.x licensing assumptions should not be carried forward. Deployment: Start with a hosted trial when the goal is to compare coding results. Self-hosting is a separate infrastructure decision. Kimi K2.x and K3 are distinct releases; record the exact model identifier rather than treating the family name as one interchangeable model. Source: [Kimi K3 model card, license, and serving guidance](https://huggingface.co/moonshotai/Kimi-K3). ## Meta Llama 4 Llama 4 Scout is worth evaluating for long-context experiments. Its advertised 10-million-token context window is a capacity claim, not proof that it is the best coding model. Best for: Developers investigating whether a large context window improves retrieval and task completion on their own code. Main limitation: Scout has 109 billion total parameters, despite activating 17 billion per token. Memory requirements extend beyond model weights, and the Llama 4 Community License has conditions that need review. Deployment: Use a supported hosted endpoint or hardware sized for the chosen build and context. Test progressively larger inputs rather than assuming maximum context is practical on a workstation. Source: [Llama 4 Scout model card and license](https://huggingface.co/meta-llama/Llama-4-Scout-17B-16E-Instruct). ## OpenAI GPT-OSS OpenAI’s gpt-oss-20b is a candidate for local coding experiments when downloadable Apache-2.0 weights and a smaller deployment matter. The larger gpt-oss-120b is a separate hardware decision. Best for: Developers who want to evaluate or customize a locally deployed model under Apache-2.0 terms. Main limitation: Fitting a model in memory does not establish its coding quality. Review generated changes and measure performance on the intended tasks. Deployment: OpenAI documents gpt-oss-20b running within 16GB of memory with its MXFP4 weights; allow for your runtime, context, and other applications. Use a compatible runtime that handles the required response format. The documented gpt-oss-120b deployment uses an 80GB GPU. Source: [OpenAI gpt-oss model card and inference guidance](https://huggingface.co/openai/gpt-oss-20b). ## OLMo Ai2’s OLMo family is worth evaluating when access to training material and reproducibility matters more than choosing a coding leaderboard winner. OLMo 3 includes 7B and 32B instruction-tuned variants alongside other checkpoints. Best for: Researchers and developers who need to inspect training resources or study and adapt model behavior. Main limitation: Openness alone does not establish coding performance. I would choose OLMo for reproducibility requirements rather than make it the default recommendation for the strongest coding output. Deployment: Select an exact checkpoint and compatible runtime for the available hardware. A base model and an instruction-tuned model serve different purposes; check the release documentation before building a coding workflow around either. Source: [Ai2 OLMo model variants, training resources, and documentation](https://allenai.org/olmo). ## How to Access These Models OpenRouter is one way to access supported open-weight models through an API. It's a unified gateway that gives you access to hundreds of models from dozens of providers through a single endpoint and API key. Instead of signing up separately for Alibaba Cloud (Qwen), DeepSeek, Meta AI, and every other provider, you put one key in your client and switch models by changing a string. For terminal agents like [OpenCode](/ai-coding-tools/opencode/), the setup is a one-line config change pointing at OpenRouter with your preferred model slug. For local use, Ollama handles model downloads and a simple local server, when a compatible model package is available. The [OpenRouter quickstart](https://openrouter.ai/docs/quickstart) explains its endpoint and authentication. Confirm the exact model is listed before configuring a client. ## Running Locally Published benchmark results apply to the exact model, precision, harness, and settings used in that evaluation. A local quantized build can behave differently. Quantization reduces weight storage, but the effect on coding reliability depends on the model and task. I use a 32GB machine, so local memory is a real constraint for me. Model weights are only part of the requirement: the context cache, runtime, editor, browser, and build tools need room too. Start with a smaller supported model and measure memory pressure, output speed, and task success before paying for hardware. Total parameters matter for storage even when only a subset is active per token. Qwen3.6-27B on a 64GB Mac remains the original starting point I recommended, rather than a universal minimum or a fresh benchmark claim. Compare newer compatible Qwen releases on the same task. Smaller machines can still handle useful local work, but the acceptable model and context depend on what you need it to do. See the [open source versus proprietary guide](/ai-coding-tools/comparisons/open-source-vs-proprietary/) for the operating-cost decision. ## Benchmark Context Coding benchmarks measure a specific model and agent setup on a defined set of tasks. SWE-bench tests whether an agent can resolve repository issues; its Verified subset contains 500 human-validated problems. A reported percentage needs the benchmark version, model version, tools, retry budget, and date beside it. There is no universal plain-versus-agentic score adjustment. Different harnesses can search files, execute commands, and retry in different ways. Use published results to shortlist models, then run the same bounded task in [OpenCode](/ai-coding-tools/opencode/) or your chosen client. Compare the diff, passing checks, review effort, and cost. ## A Note on Benchmark Dates Model-source links and selection guidance were reviewed on October 17, 2026. This shortlist preserves the original deployment priorities without presenting midyear scores as current rankings. Model versions, hosted prices, and available quantizations change independently. Record the model identifier, runtime, context setting, permission configuration, and test date beside each result. A model earns its place when it completes the work with an acceptable bill and review burden, not when its name appears at the top of an unrelated chart. ## Related Reading - [Open source vs proprietary AI coding tools](/ai-coding-tools/comparisons/open-source-vs-proprietary/): the hardware reality, the platform risk lesson from OpenCode, and a decision framework - [OpenCode guide](/ai-coding-tools/opencode/): how to configure the open-source terminal agent with your own model choices - [AI coding tool comparisons](/ai-coding-tools/comparisons/): full hub with all head-to-head matchups Selection guidance and linked model documentation reviewed on October 17, 2026. Personal examples describe the author's original experience. --- ## Claude Code vs OpenAI Codex URL: /ai-coding-tools/comparisons/claude-code-vs-codex/ Cluster: AI Coding Tools > Comparisons I use Claude Code for an interactive plan-and-review workflow and Codex for delegated tasks and an independent review. Both can inspect a repository, edit several files, run checks, and work through terminal, desktop, IDE, or web surfaces. Choose the one that fits your account and review habits first; add the other only when it earns a separate job. There is a useful real-world frame for this matchup. I run Claude Code and Codex as two parallel agents, with separate scopes and an independent review loop. That experience does not make either tool universal, but it shows why the comparison is not about a one-line benchmark. It is about whether the agent gives you a workflow you can supervise. | Dimension | Claude Code | OpenAI Codex | | Ecosystem | Claude (Anthropic) | ChatGPT (OpenAI) | | Shape | Interactive or parallel tasks across supported surfaces | Desktop, CLI, IDE, and cloud tasks + IDE extension | | Context | Varies by model and account | Varies by model and surface | | Sandbox | Starting mode varies by plan; OS-level with `/sandbox` | Configurable sandbox and approval policy | | Background agents | Yes (Web + Agent View, parallel) | Yes (Codex Cloud, parallel) | | Open source | No | Yes (Rust CLI) | | Best at | Interactive, plan-first pair work | Delegated, parallel, reviewable work | If neither workflow fits, use our [AI coding tool recommendations](/ai-coding-tools/) to compare the wider field. ## How They Work Claude Code is Anthropic's coding agent, available through the terminal, VS Code, Claude Desktop, and web. The native installer is the recommended terminal setup. In a local session it reads the project and runs tools on your machine, while selected content goes to the configured model service. Permission settings control tool actions; the interface alone does not define those permissions. Codex is OpenAI's coding agent across desktop, CLI, IDE, and cloud workflows. Its Rust CLI is open source, so that client can be inspected and modified. That does not make the hosted models or every Codex service open source. A local task and a cloud task also run in different environments, which affects available files and credentials. Both products support interactive work and delegated tasks. Claude Code's planning and repository guidance fit my implementation loop; Codex gives me a separate model and review workflow. Neither is limited to one session or one interface. > The useful question is not which agent can edit a file. It is which review loop you will still trust after the first difficult change. ## Ecosystem Choice This is the decision that matters most in practice. Picking a terminal agent here really means picking which AI ecosystem to center your development workflow around, since the agents come bundled with the subscription. Claude Code is tied to the Claude subscription. If you already pay for Claude Pro or Claude Max, Claude Code access is included and your usage counts against your plan's limits. Codex can use an eligible ChatGPT account or a separately billed API credential. Free and Go currently have limited Luna access in desktop; paid plans broaden access and usage. Claude Code similarly offers subscription and API routes. Choose the account path deliberately because its limits, billing, and cloud features differ. That makes the question simpler than the spec sheets suggest. If your team already lives in one ecosystem for writing, analysis, and research, the matching agent has a real gravity that the competing tool has to overcome. ## Context and Memory Claude Code supports a `CLAUDE.md` file for persistent project context and has tools for continuing or compacting long sessions. Codex reads `AGENTS.md` for project guidance and supports session continuity. The published context limit can differ by model, account, and product surface, so treat a large number in a plan page as a limit to verify, not a reason to skip a real test. For most repositories, the practical test is simpler. Give each agent the same bounded task, include the same repository guidance, and check whether it finds the right files, explains its assumptions, and leaves a reviewable diff. That reveals more than a context-window headline. ### Published Limits Are Not a Workflow Test Anthropic and OpenAI publish plan and product documentation, but an account entitlement does not show whether either agent will find the right boundary in one repository. Use the [current Claude plan page](https://claude.com/pricing) and [OpenAI's Codex documentation](https://openai.com/index/introducing-codex/), checked July 28, 2026, for access questions. Use the same small task in both tools for the operational decision. ## Autonomy and Approval Claude Code and Codex both separate tool authority from the task you type. Check the active approval policy and sandbox before a run, rather than assuming either agent always asks or always acts autonomously. Current Claude Code terminal and VS Code sessions use auto mode by default on supported installations and account routes, subject to model availability and managed or user settings. Auto mode assesses tool actions; it is not the same as bypassing permissions. Other surfaces, including Desktop, have their own setup choices. Codex exposes sandbox boundaries such as read-only or workspace-write alongside an approval policy. A sandbox limits what tools can access; the approval policy determines when a request needs authorization. The older suggest, interactive, and full-auto labels are not a reliable description of every current client. Claude Code's sandbox adds operating-system isolation for supported commands. In either product, inspect the actual settings and test a harmless denied action before giving an agent broader access. A passing permission test does not replace reviewing the resulting diff. ## External Tools and Background Work Both support the Model Context Protocol, so they can connect to databases, project trackers, and APIs. The ecosystems overlap heavily, but an integration is never a free win. Check the server, its permissions, and what data it can expose before connecting it to a production project. Both products offer background and delegated work. Their available surfaces, time limits, and parallel-task controls change, so verify the current vendor documentation before setting a team process around a specific ceiling. ## Model Quality Both products evolve their model choices quickly. Compare the agent surface, permissions, and review loop on a real task instead of treating a model name as a permanent winner. The model can change before a buying decision reaches procurement. Keep the comparison record with the task: account route, selected model, permission mode, files changed, commands attempted, and checks run. This record makes a later re-test possible when a vendor changes a model, plan, or client surface. It also separates an access limitation from a weakness in the agent workflow. Use a task with an observable result, such as a failing focused test or a known form bug. Keep credentials, migrations, and bulk operations out of the comparison. The result should show whether the agent followed the boundary, not whether it was allowed to reach a risky part of the repository. Repeat the same task after a material client or model change. ## Pricing Claude Pro and ChatGPT Plus each cost $20 per month and include their respective coding agent, with usage limits. Claude Max has $100 and $200 monthly tiers; ChatGPT Pro has $100, $200, and $500 tiers. Separately billed API routes are also available. These US monthly prices were checked October 16, 2026. | Tier | Claude (Claude Code) | ChatGPT (Codex) | | Entry | Pro, $20/month | Plus, $20/month | | Higher usage | Max 5x, $100/month | Pro, $100/month | | Additional higher usage | Max 20x, $200/month | Pro, $200 or $500/month | Matching monthly prices do not mean matching allowances. Free and Go offer limited Codex Luna access in desktop, while broader Codex access depends on the paid plan. Use the [Codex pricing guide](https://learn.chatgpt.com/docs/pricing) and [Claude plan page](https://claude.com/pricing) to compare the account route you will actually use. ### Check the Meter Before You Compare Costs Both vendors now make the account route part of the price. Anthropic documents Claude Code access through the Claude app, the Console, or enterprise platforms such as Bedrock and Vertex AI. OpenAI's [Codex rate card](https://help.openai.com/en/articles/20001106-codex-rate-card) says current plans meter Codex through credits based on input, cached-input, and output tokens. That means two people on the same named plan can see different consumption from the same-looking task. For a fair comparison, run one normal task and one task that fails a check on purpose. Record the account type, model, task size, elapsed time, retries, and remaining allowance after each run. A provider's monthly headline is useful for budgeting. The meter and the review time tell you what the workflow costs. ## Where Each One Wins Claude Code wins on the interactive, plan-first session. Plan mode, the CLAUDE.md configuration system, and the way Claude communicates all fit a workflow where you're pair programming with the agent rather than dispatching it. A disciplined loop is to describe the work, review the plan, approve or adjust it, inspect the multi-file changes, and compact the conversation only when the context has become unwieldy. Codex wins on delegation and team integration. Codex Cloud's dispatch-a-task-and-review-the-PR style is a different flavor of background work from Claude Code's Web and Agent View sessions, the open-source CLI is appealing if you value being able to inspect and modify your tooling, and the `@codex` PR-comment trigger fits teams that already run their review process through GitHub. The cloud paradigm is a different mode of working, where you're handing off tasks rather than sitting beside the agent the whole time. ### One Trial, Run Safely in Both Tools Use the same acceptance test for both agents. Write a small issue with one user-facing outcome, a file boundary, and the checks that prove it. A good candidate: update a form validation rule, add a focused test, and explain the changed files. Ask each agent to explain its plan before it edits, then inspect the diff without reading its summary first. The changed files should make sense even when the sales pitch is removed. Keep the first run away from production credentials, migrations, and bulk deletion. Claude Code documents permission controls for commands, while Codex exposes approval and sandbox choices through its client surfaces. Those controls reduce accidental reach; they do not turn an unclear task into a safe one. - Save the exact prompt and repository guidance used. - Record the files changed before accepting the summary. - Run the build or focused test yourself and read any failure. - Give the other agent one concrete review question about the diff. That last step is where a two-agent setup earns its keep. Asking an independent agent to find a missing test, permission problem, or scope leak is more useful than asking it to declare the first agent correct. It also leaves a trail a human reviewer can follow when the two answers disagree. In my own setup that division has settled into a pattern: one agent owns the change, the other is only ever asked pointed review questions about it, and neither reviews its own work. The reviewer role costs little, because a focused question about one diff is cheap, and it regularly surfaces the kind of miss a summary hides. If you adopt only one habit from this comparison, make it that one. Keep the trial deliberately ordinary, and time-box it. Two runs on the same task expose how an agent searches, handles a failed command, and explains an awkward requirement; repeating a polished demo until it succeeds only measures your patience. Keep the baseline task in the repository as a small fixture, so a new model or client release can be checked against the same evidence later. Watch for hand-off failures. A terminal agent may complete the code but leave an unexplained configuration change, a broad formatting diff, or an instruction for a human to finish the dangerous part. Those are useful findings from a trial because they tell you where the tool needs tighter project guidance or a smaller scope before it touches work with release consequences. And keep a human in the loop at the point where a change becomes expensive to undo: a passing unit test does not prove that a migration preserves customer data. ## Verdict: Claude Code or Codex? Choose Claude Code for the interactive planning and repository work we prefer; choose Codex when its delegation and review workflow fits you better. | Choose Claude Code if | Choose Codex if | | You sit beside the agent | You dispatch and review later | | You want plan-then-approve control | You want an inspectable CLI | | Multi-file edits need close reading | Review runs through GitHub comments | | Your account path is Anthropic | Your account path is OpenAI | Run both against the same non-production task, inspect every diff, and keep the workflow that produces changes your team can understand and safely ship. Most teams that use both settle on one owner of the change and one independent reviewer. ## Claude Code vs Codex FAQ ### Is Claude Code or Codex better for a terminal workflow? Claude Code is our preferred choice for an interactive terminal planning-and-review loop; Codex is a strong alternative when background tasks and an independent review workflow matter more. Run the same small task in each before moving a team workflow. ### Can Claude Code and Codex work on the same project? Claude Code and Codex can work on the same project, provided the team gives each agent clear scope and reviews the resulting diff. A practical split is to use one agent for an implementation pass and the other for an independent review. Do not let two agents edit the same files without coordination. ### Do I need a separate API account for Claude Code or Codex? Claude Code and Codex do not require a separate API account when you use an eligible app subscription; direct API billing uses a separate provider account or credential. Claude Code supports Claude app subscriptions, the Anthropic Console, and some enterprise platforms. Codex access also varies by OpenAI product and account. Check the current plan and account documentation before choosing based on price or usage limits. ### Which is easier to trust with repository changes? Neither Claude Code nor Codex is inherently easier to trust with repository changes; trust depends on the permissions, checks, and review process you apply. Start with a narrow task, inspect the plan and diff, run the project checks, and only then widen the permissions. Keep credentials and destructive commands outside an agent's default reach. ### What should I compare during a Claude Code and Codex trial? A Claude Code and Codex trial should compare the changed files, commands run, failed checks, review time, and any assumptions the agent made. A faster first response is less useful than a small, explained diff that passes the same project checks in both tools. ### Can a background agent replace code review? A background coding agent cannot replace human code review. It can prepare a branch or flag a risk, but a person still owns the release decision. Keep the task bounded, inspect the resulting diff, and reproduce a reported issue before accepting a change or blocking a pull request. ## Read the Full Guides For the full picture, read the [Claude Code](/ai-coding-tools/claude-code/) and [OpenAI Codex](/ai-coding-tools/codex/) deep-dive hubs, then browse the [AI coding tool comparisons](/ai-coding-tools/comparisons/) hub for the other head-to-heads. ## Next If you want to see how Claude Code stacks up against an open-source alternative, read [Claude Code vs OpenCode](/ai-coding-tools/comparisons/claude-code-vs-opencode/). Product details and pricing checked against official documentation on October 16, 2026. Personal examples describe the author's original experience. --- ## Claude Code vs GitHub Copilot URL: /ai-coding-tools/comparisons/claude-code-vs-copilot/ Cluster: AI Coding Tools > Comparisons I prefer Claude Code for repository-wide implementation and reasoning. GitHub Copilot is the better fit when inline suggestions, existing editor support, and GitHub administration decide the choice. Both can edit several files and run agents; Claude Code also has desktop, IDE, and web surfaces, so you do not need a terminal-only workflow to use it. The practical choice is between help built into an existing editor rhythm and an agent you can direct through a larger piece of work. That distinction stays useful even when both products expose similar model families. | Area | Claude Code | GitHub Copilot | | Work surface | Terminal task | Editor and GitHub | | Review unit | Task diff | Suggestion, diff, or pull request | | Model path | Claude | Several model providers | For recommendations beyond this pair, see our [best AI coding tools](/ai-coding-tools/) ranking. ## What the Products Include Copilot runs in several editors and GitHub surfaces, so no single editor represents the whole product. Compare the editor and pull-request workflows you actually use with Claude Code’s supported interfaces. Where you work matters as much as the model. Copilot provides completions, chat, agent work, and review in editors and GitHub. Claude Code begins with a terminal task, then reads files, proposes or makes edits, and runs approved commands. Both can touch several files, so calling one an "assistant" and the other an "agent" no longer explains what each can do. Claude Code offers a desktop diff viewer and IDE integration as well as its terminal client. You may still want a full editor for manual changes, browsing files, and debugging. The distinction is whether you primarily want inline suggestions or task-oriented agent work. ### Use One Task Brief Start both tools from the same commit. Give them the same expected result, paths in scope, paths that must stay untouched, and proof command. Put repository instructions in files both tools can read rather than explaining extra rules to only one run. Use a task that needs more than autocomplete but does not require production access. A small bug with a known reproduction and regression test works well. It tests search, edits, commands, and review without turning the trial into a release. Record every human hint because a selected file for Copilot or a likely cause for Claude Code makes the trial unequal. The hint may still be fair if it matches ordinary use, but it belongs in the result. Compare the final diff, check output, retries, and review time. Do not score the prose explanation or time to first response on their own. A tool earns credit when the task reaches a state another developer can verify. ## Philosophy Gap Copilot can stay close to the line you are editing, and it also has cloud and pull-request agents. Claude Code can take a larger terminal task and works beside an editor, though it still stops for approval when its permission mode or safety checks call for it. The useful difference is where you supervise the work. Copilot supervision often happens beside the current file or pull request. Claude Code supervision often happens through the task brief, command approvals, terminal output, and final diff. Neither path removes the need to understand the patch. For a typo or a small completion, the gap barely matters. For a feature that touches ten files, a module refactor, or a subtle bug spanning several files, the workflow matters more. Copilot can keep you in the editor while you steer the work. Claude Code can take a whole bounded task, provided you still inspect the result and run the checks. ## Repository Context Copilot assembles repository context according to the feature and surface in use. Editor chat can begin from the workspace, selected code, open files, and tools. Pull-request review can gather more context through agentic infrastructure. Check the current GitHub docs for the exact data and billing path of the feature you enable. Claude Code searches the repository with file and shell tools as the task runs. It can follow imports, search symbols, and read test output without a separate indexing step. Selected content still goes through the configured Anthropic account, so on-demand search is not the same as local-only processing. Test whether each tool finds related code by giving it a task that changes one part of the project and affects another. Record which files each tool edited, which related files it missed, and whether the project checks exposed the gap. A summary answer about the repository is weaker evidence than a correct, reviewable diff. ### Audit the Context Boundary List the inputs each surface can read before the trial: selected code, open files, repository search, issue text, pull-request comments, tool output, and external services. The list changes when a task moves from editor chat to cloud review. Use harmless sentinel text in a permitted file and a denied file, then ask the agent to find both. Save the response and the account or policy setting that should block the denied path. Check the remote path separately because pull-request review may gather context through GitHub Actions, while a local terminal task uses local file and command permissions. Those two runs can see different data even when they discuss the same branch. Review retention and billing for the gathered context. A file absent from the final patch may still have entered a prompt, log, cache, or Actions artifact. The account policy must cover those copies as well as committed code. ## Model Experience Gap Copilot can expose several model choices while Claude Code is designed around Claude. GitHub updates Copilot model availability separately, so check which models you can select today rather than assuming the list stays fixed. [GitHub's models and pricing reference](https://docs.github.com/en/copilot/reference/copilot-billing/models-and-pricing), checked July 28, 2026, prices model use by input, cached input, and output tokens, then converts that use into AI credits at US$0.01 per credit. Paid-plan completions and next-edit suggestions do not consume those credits. The selected model and task size control how fast an allowance is used. The same prompt can cost more when it includes a larger repository context or produces a longer answer. Copilot code review is a special case because GitHub selects the model rather than exposing a picker. | Model | Relative cost to run | | Small context | Fewer input tokens | | Large repository context | More input tokens | | Long generated patch | More output tokens | | Code review | AI credits and Actions minutes | Set a budget before enabling paid overage. Separate completion use from chat, agents, CLI work, and code review in the usage report. A week dominated by inline completion will not predict the bill for cloud-agent tasks. Auto selection and the model picker can produce different cost and quality trade-offs. Before treating a poor result as a verdict on Copilot, record which model, mode, repository context, and task you used. That makes a trial repeatable rather than anecdotal. Claude subscriptions use plan limits rather than Copilot's per-token AI-credit ledger, and Anthropic sells API use separately. Track which account route powers Claude Code, because an individual plan, team seat, and API credential do not create the same bill or policy. ### Test the Usage Budget Set a small overage budget before the first Copilot agent or review pilot. Run one editor-chat task, one cloud-agent task, and one pull-request review. Record the credits and Actions minutes attributed to each surface. For Claude Code, record the account type, session pauses, and any API-billed work. A subscription limit is not a token invoice, so the fair comparison is whether the plan carried the representative workload without hiding wait time or a second account. Include a failed task because retries can consume credits, plan capacity, and reviewer time without producing a usable patch. The failure record shows whether a cheap success was typical or an outlier. Keep model names and dates in the worksheet. GitHub can revise available models and per-token rates, while Anthropic can revise plan limits. The worksheet should be easy to rerun after either vendor updates the account. ## Pricing, Side by Side Copilot Pro costs $10 per month, Pro+ $39, and Max $100; Business and Enterprise cost $19 and $39 per user per month. Claude Pro costs $20 per month and Max $100 or $200. Claude Team Standard is $25 per seat monthly and includes Claude Code; Premium is $125 per seat monthly for higher usage. Check the [Copilot](https://github.com/features/copilot/plans) and [Claude](https://claude.com/pricing) plan pages for annual billing and usage conditions. Prices checked October 16, 2026. | Tool | Plan | Price | What you get | | Copilot | Pro | $10/mo | $15 total monthly credits | | Copilot | Pro+ | $39/mo | $70 total monthly credits | | Copilot | Max | $100/mo | $200 total monthly credits | | Claude | Pro | $20 monthly | Claude Code and plan limits | | Claude | Max | from $100/mo | 5x or 20x Pro usage | | Claude | Team Premium | $125 monthly | Claude Code and team admin | Do not turn the table into a team quote. Copilot organization plans pool credits under admin budgets, while Claude Team mixes standard and premium seats. Ask both vendors for the current seat, usage, data, and contract terms that apply to your account. ### Price the Work and the License GitHub's [current billing documentation](https://docs.github.com/en/billing/concepts/product-billing/github-copilot-billing) defines one AI credit as US$0.01 and says individual plans include a monthly allowance, making the arithmetic inspectable. Model selection and task size still alter how quickly the allowance goes. A useful team worksheet has four columns: licence cost, AI-credit use, any overage, and the time needed to review or repair the output. Include code review because a low-cost plan can still leave developers spending time sorting useful findings from false alarms. - Set an organization budget before enabling overage. - Separate completion use from agent and review use. - Test one ordinary feature and one failed attempt. - Check the live plan page before a renewal decision. GitHub separates input, cached input, and output token prices, so track a representative week of use. A trial that only accepts inline completions will not predict the cost of code review or cloud-agent work. GitHub's code-review documentation adds a second cost to the review path: AI credits for the model interaction and GitHub Actions minutes for agentic context gathering. That detail matters when a team enables automatic reviews. The review may help, but it also needs a budget, a way to prioritise comments, and a person who can tell a useful finding from a false alarm. Do a small pilot before enabling automatic reviews everywhere. GitHub's own documentation recommends a limited selection of repositories while developers get used to the feature. Measure the useful findings, the false alarms, and the time spent responding. Those results tell you whether the reviews save time. ## Where Copilot Wins Copilot does several things Claude Code does not attempt. Inline completions while you type are the obvious example: accept a suggestion and keep moving. Claude Code is not built for that rhythm. Claude Code asks you to frame a task and wait for a plan or result, which is a different way to work rather than a failure of the terminal approach. Copilot can enter an existing editor and GitHub workflow without adding a separate terminal habit. Pull requests, issue references, repository policy, and billing remain in GitHub. Teams still need to test editor compatibility, data policy, model access, and review cost before enabling it across every repository. ## Where Claude Code Wins Claude Code suits multi-file work such as adding authentication to an admin panel or finding why discount codes fail at checkout. Those tasks give an agent room to trace how files relate, form hypotheses, and run checks. The result still needs a human reviewer, especially when a patch crosses security, payments, or data boundaries. Code quality depends on the task, the repository guidance, and how carefully people check the changes. Claude Code can plan, edit, test, and prepare a patch from one description, but that convenience should not become automatic trust. Require a reviewable diff, project checks, and a review that can explain why each edit belongs. ## Code Reviews Both tools can participate in pull-request review, but no review bot should be the final gate. GitHub documents that Copilot code review can use project-context gathering and different review-effort levels, with associated AI-credit and GitHub Actions use. Claude Code can inspect a diff and help apply a confirmed fix. In both cases, a reviewer still needs to reproduce the concern in the code. ### Score the Review Pilot Select a small group of pull requests with known outcomes. Include a correct patch, a seeded regression, an irrelevant large diff, and a docs-only update. Ask the review tool to inspect them under the same repository instructions. Classify each comment as confirmed bug, useful question, style preference, duplicate, or false alarm. Record the time needed to reproduce and close it. Comment count is not a quality measure because one false alarm can take longer than several useful findings. Track missed seeded bugs as well as raised comments. A quiet review is not proof of correctness. The pilot needs both precision and recall evidence before automatic review is enabled on every pull request. Do not let a bot comment block a merge until a person can connect it to code behaviour, a requirement, or a failing check. If the claim cannot be reproduced, close it with the evidence rather than changing code to satisfy the wording. ## Verdict: Claude Code or Copilot? Claude Code is our number-one AI coding tool and the stronger pick here for most readers; Copilot has dropped down our rankings and nobody on our team runs it daily any more. Choose Copilot only when editor suggestions, GitHub-native agents, and pull-request review must share one centrally administered account and billing system. Choose Claude Code when a task-led workflow and repository-wide reasoning match the work, which for us is most of the time. Review time counts in either bill. ## Claude Code vs Copilot FAQ ### Is GitHub Copilot an alternative to Claude Code? GitHub Copilot is an alternative to Claude Code for coding assistance and agent tasks, although their integrations and account workflows differ. Copilot is integrated into supported editors and GitHub workflows, while Claude Code is a task-oriented agent across terminal, desktop, IDE, and web. Both can help with multi-file work, so compare them on a representative task instead of treating either label as a capability guarantee. ### Can GitHub Copilot review pull requests? GitHub Copilot can review pull requests on eligible paid plans and supported surfaces. The review can use AI credits and GitHub Actions minutes for agentic context gathering. Treat every finding as a prompt for human investigation, not an automatic approval or rejection. ### Does Claude Code replace VS Code or another IDE? Claude Code does not replace every IDE feature, although it can handle coding tasks through terminal, VS Code, desktop, and web surfaces. Choose by the tasks and review tools you need, rather than a terminal-versus-visual label. ### How should a team compare Claude Code and Copilot costs? Teams should compare Claude Code and GitHub Copilot costs by consulting current provider plan pages and modeling a normal month of chat, agent, and code-review use. Copilot pricing can include AI credits and code-review infrastructure usage. Claude Code access can come through different account types. Published allowances move, so do not choose from an old comparison table alone. ### Do Copilot AI credits replace a review budget? Copilot AI credits pay for tool usage, not the engineering time needed to review its output. Track which model and feature consumed credits, then include the time spent checking a suggestion, fixing a failed test, or investigating a review comment when you compare plans. ### How can I trial Copilot without distorting the cost comparison? Developers can trial GitHub Copilot fairly by using a fixed repository task, recording the selected model and mode, and comparing credits consumed with the review time and any repair work. GitHub bills advanced usage by token-based AI credits, so a completion-heavy week is not comparable with a cloud-agent-heavy week. ## Read the Full Guides For the full guides, the [Claude Code](/ai-coding-tools/claude-code/) hub covers setup, workflows, and where the agent model pays off, while the [GitHub Copilot](/ai-coding-tools/copilot/) hub walks through the extension, its plans, and the credit system in detail. You can browse every head-to-head on the [AI coding tool comparisons](/ai-coding-tools/comparisons/) hub. ## Next If you're weighing terminal agents specifically, read [Claude Code vs OpenAI Codex](/ai-coding-tools/comparisons/claude-code-vs-codex/) for the closer fight. Product details and pricing checked against official documentation on October 16, 2026. Personal examples describe the author's original experience. --- ## Claude Code vs OpenCode URL: /ai-coding-tools/comparisons/claude-code-vs-opencode/ Cluster: AI Coding Tools > Comparisons Choose Claude Code when you want an Anthropic-focused coding agent with a direct subscription or API setup. Choose OpenCode when open client code, multiple model providers, or local inference matters more. Both offer terminal and visual surfaces; the central difference is who controls the model and the infrastructure around it. Claude Code is the one of these two I know from long use, while my time in OpenCode has been short. The OpenCode claims below come from its published documentation and source repository rather than long personal use. | Area | Claude Code | OpenCode | | Models | Claude models | Hosted and local providers | | Configuration | Anthropic account path | Provider-by-provider setup | | Client | Proprietary | Open source | Version scope: OpenCode permission and provider examples below refer to V1. V2 changes the runtime and configuration structure. Follow its migration documentation for your installed version. ## Background A client, a model subscription, and API access are separate commercial relationships. OpenCode's [provider docs](https://opencode.ai/docs/providers/), checked July 28, 2026, list different authentication routes for different providers. Some use API keys, some use OAuth, and some read cloud credentials from the operating system. Do not assume that a consumer subscription grants a third-party client API access. Use the route named in the provider's current docs, record which account is billed, and test what happens when that credential expires. A workflow can break when the client, provider, or account rule changes. The separation also affects incident response because a rejected request may come from the client configuration, a revoked credential, a model name, an account limit, or the provider service. > Before you build a workflow around a model, know which account, credential, and provider agreement makes it work. ## How Each One Works The day-to-day experience is similar on the surface. You run either tool in a terminal, describe a task, inspect proposed work, and review the changed files. The differences sit underneath that shared command-line surface. Claude Code is built by Anthropic around Claude models. Its account, model, tool, and permission docs come from one vendor. The narrower model path reduces provider setup, while keeping the workflow tied to Anthropic's service and terms. OpenCode lists many built-in and custom providers, including local routes. You select the provider, model, and credential path. The open client makes that configuration inspectable, but switching still requires a compatible model, tested prompts, working credentials, and a new data-policy review. ### Trace One Request Start with a harmless repository and one provider. Record the command that starts the client, the configuration files it reads, the credential source, the model identifier, and the endpoint that receives the request. Capture the provider's account log beside the client transcript. The two records should agree on time, model, and usage. If they do not, check for a gateway, alias, fallback, or second credential before trusting the billing report. Repeat the trace after switching providers because a client-side model name can map to a different provider name or token rate. Keep the final identifier and endpoint in the trial record so another developer can reproduce the route. Claude Code needs the same account trace. Note whether the session uses an individual plan, team seat, cloud integration, or API credential. "Claude Code" names the client, not the contract that paid for the request. ## Pricing OpenCode's client is free. Hosted model usage costs extra unless you select a free model, and local inference needs your own hardware. Optional Go subscriptions cost $10 per month or $40 for Go Plus. Claude Code is included with Claude Pro at $20 per month and Max at $100 or $200, or can use separately billed API access. Prices checked October 16, 2026. OpenCode can use provider credentials you configure, and its [Zen docs](https://opencode.ai/docs/zen/) describe an optional selected-model gateway. The docs and [Claude plan page](https://claude.com/pricing) should be checked again before purchase because rates and included use can move. | OpenCode path | What it is | Cost | | BYOK | Your provider credential | Provider invoice | | Zen | OpenCode gateway | Credit balance | | Provider plan | A provider-specific subscription or account | Check the current provider terms | | Local | Self-run inference | Hardware and operations | ### Build a Usage Worksheet Use a month of representative tasks rather than invented token totals. Record the provider, model, input and output use, retries, cache charges, review time, and any failed run. A flat plan and a metered route cannot be compared from seat price alone. Set a provider budget before the trial. Keep ordinary work on one model long enough to measure it, then test a second provider against the same task. If the model changes with every prompt, the cost record cannot explain which route produced the useful diff. Include operator time for local inference in the worksheet alongside downloads, storage, runtime updates, model swaps, slow failures, and hosted token charges. A zero-dollar API line is not a zero-cost workflow. ## OpenCode Advantages OpenCode's [agent docs](https://opencode.ai/docs/agents/), let each named agent set its model, prompt, tools, and permissions. One project can use different providers for named tasks. Claude Code also has subagents, but its model path remains within Claude. OpenCode splits client and server concerns, which can place the agent process on another machine. That option adds network exposure, authentication, logs, and host patching to the review. Do not expose an agent server until the access boundary is documented and tested. OpenCode exposes terminal, desktop, editor, web, and API surfaces. Claude Code also reaches desktop, editor, web, and GitHub flows. Count the surfaces you will secure and maintain. More entry points help only when the team owns their credentials and can trace which one changed the repository. ### Configure Permissions Before the First Run OpenCode's [permissions documentation](https://opencode.ai/docs/permissions) gives each tool an `allow`, `ask`, or `deny` rule. The published example starts by asking for shell access, allows only named safe commands, and blocks destructive patterns. That is a better starting point than accepting the defaults and hoping the prompt stays narrow. Put the safety boundary in configuration as well as prose. A project instruction can say "do not edit deployment files," but an edit deny rule can enforce it. Keep access outside the working directory blocked unless there is a specific, reviewed reason to widen it. OpenCode also documents separate rules for subagents, which matters when a task delegates research or review. - Allow reads in the project, then restrict edits to the intended paths. - Ask before shell commands until the command patterns are understood. - Deny commits, pushes, and secret-bearing files in an initial trial. - Use provider credentials outside the repository and rotate any exposed credential. The open client gives you a visible configuration surface. It does not make every provider connection equivalent. OpenCode's provider documentation lists different authentication paths, model catalogs, and routing options, so check the selected endpoint's retention and billing terms before putting source code through it. ### Compare Permission Rules Write the same safety policy for both tools before translating it into configuration. The policy should name readable paths, editable paths, approved commands, blocked files, network destinations, and actions that always need a person. OpenCode and Claude Code both document allow, ask, and deny ideas, but their match syntax and defaults differ. Test the resulting behaviour rather than assuming the same words create the same boundary. Use harmless sentinel files inside and outside the project. Ask each agent to read, edit, and list them. Then test one denied shell pattern and one denied network destination, and save the exact refusal message. Run the check again for a subagent. A parent agent may have a narrow policy while delegated work uses another tool set. The boundary is complete only when every agent path produces the expected denial. ### Provider Choice Needs a Written Record OpenCode's provider docs describe API credential, OAuth, and operating-system credential routes. The selected provider determines the endpoint, so switching a model can also alter data handling and billing. A flexible picker does not give every route the same retention policy or account controls. Write down four details before the trial: the provider, model identifier, authentication method, and the person who owns the spend limit. If a later incident requires you to know which service received a repository prompt, those details are more useful than a generic entry that the team used an open-source client. That record also makes a clean comparison possible. Run the same bounded task with one provider at a time, keep the permissions identical, and compare the diff and check results before comparing the model's prose. Provider choice is useful when it remains visible to the people who approve the work. Run the first task with one provider and one model. When it fails, capture the model, provider, cost, permissions, and last command. A later switch can otherwise hide the cause instead of fixing it. Apply the same record to local models. Confirm where the runtime listens, where model files live, and whether any fallback sends a prompt to a hosted endpoint. Name one provider for ordinary work and document every exception. ## Claude Code Advantages Claude Code's narrower provider path means fewer credential and endpoint choices during setup. Its [permission docs](https://code.claude.com/docs/en/permissions), describe allow, ask, and deny rules plus managed policies. The account choice still affects billing and data terms. Before relying on either agent, set the allowed paths, command rules, required checks, and recovery method. Keep a Git history another developer can inspect. A convenient terminal run should not become an opaque patch. ## The Local Model Reality Check OpenCode can connect to local model servers, but the client cannot promise that a chosen model fits your hardware or passes your task. Memory use varies with the model build, quantisation, context setting, runtime, and concurrent dev tools. Test on the machine that will run the service. Record peak memory, output rate, time to first token, task completion, checks, and review time. Run the same repository task through the hosted route so the comparison includes diff quality alongside model size. A local route can meet an offline or network-boundary rule. It also makes your team responsible for model files, runtime updates, storage, capacity, and failures. Estimate costs from your trial, including hardware and running costs. Do not assume a local model will produce the same results as a hosted model. ### Make Local Fail Closed Disconnect the local model server during a harmless task. The client should report the unavailable endpoint rather than sending the prompt to another provider. If fallback is allowed, name the provider and require an explicit approval before it runs. Bind the local server to the narrowest required network interface. Check the listening address from another machine and confirm that authentication works as intended. A local model open to the office network is not the same as a process limited to one workstation. Restart the machine and repeat the check. Record which service starts the runtime, which model loads, where logs go, and what happens when the model file is missing. These recovery details decide whether the local route can be operated by anyone except its installer. Keep hosted and local usage records separate. That makes an accidental fallback visible on the invoice and preserves the evidence needed for a local-only data rule. ## Head-to-Head | Capability | Claude Code | OpenCode | | Models | Claude | Several providers | | Billing | Anthropic account | Selected provider | | Subagents | Claude models | Per-agent model | | Local models | Not documented | Provider option | | Open source | No | Yes | | Setup | One vendor path | Provider-specific | ### Run a Small Provider Trial Use a non-sensitive repository and write down the provider, model, credential route, and spending limit before you start. Ask OpenCode to make one bounded edit, run the same checks you would run for a human contribution, and inspect the diff. Repeat with Claude Code only if the task and project instructions are the same. - Keep provider keys out of repositories and terminal logs. - Use a small budget or spend alert for the trial. - Check whether the chosen model can complete the task without unreported retries. - Keep the successful prompt and review record for the next comparison. ## Verdict: Claude Code or OpenCode? Choose Claude Code for one Anthropic account path and a Claude-focused terminal agent. Choose OpenCode when provider choice, an inspectable client, or a tested local route is a written requirement. The accepted trade-off is fewer setup decisions against ownership of credentials, model selection, billing, and provider policy. ## Claude Code vs OpenCode FAQ ### Is OpenCode a replacement for Claude Code? OpenCode can replace Claude Code for workflows where its chosen model and tools meet your requirements, but it is not a drop-in replacement for Claude-specific configuration and account access. OpenCode can connect to many providers, while Claude Code is built around Claude. The better fit depends on whether a supported single-provider workflow or provider flexibility matters more to your project. ### Can OpenCode use local models? OpenCode can connect to local models, including through Ollama. Local use still requires you to choose a model, provide enough hardware, and accept the quality and maintenance trade-offs. Treat local capability as a requirement to test, not a blanket promise of private, frontier-quality agent work. ### Do I need API keys to use OpenCode? OpenCode needs credentials for a hosted provider, but not an external provider API key for a local Ollama model; the exact authentication method depends on the provider. Read the current provider documentation before storing credentials, and give an agent only the access it needs for the task. ### Which is simpler for a first terminal agent? Claude Code has a more direct Claude-first setup. OpenCode offers more provider choice, which also means more decisions about keys, models, and billing. Start with the tool whose setup and permission model you can explain to the next person who joins the project. ### How should I configure OpenCode for a safe first trial? For a safe first OpenCode trial, developers should start with a non-sensitive repository, require approval for shell commands and edits, deny access outside the project, and set a small provider budget. OpenCode documents allow, ask, and deny permission rules, so the initial configuration can stay narrower than a daily-driver setup. ### Does an open client remove model-provider risk? An open-source coding client does not remove model-provider risk. It can make it easier to switch providers, but each selected endpoint still has its own billing, retention, uptime, and credential rules. Record the provider and account route for every production workflow. ### Where does OpenCode store a provider credential? OpenCode 1 stores API keys added through `/connect` in `~/.local/share/opencode/auth.json`, according to its [provider documentation](https://opencode.ai/docs/providers/#credentials). Environment variables and provider-specific authentication can use a different storage route. Keep credentials out of the repository and restrict access to the credential file. ## Read the Full Guides For the full picture on each tool, read the [Claude Code](/ai-coding-tools/claude-code/) guide and the [OpenCode](/ai-coding-tools/opencode/) guide, and browse the [AI coding tool comparisons](/ai-coding-tools/comparisons/) hub when you want to weigh other matchups before committing. For the wider shortlist, see our [AI coding tools guide](/ai-coding-tools/). Product details and pricing checked against official documentation on October 16, 2026. Personal examples describe the author's original experience. --- ## Cursor vs Google Antigravity URL: /ai-coding-tools/comparisons/cursor-vs-antigravity/ Cluster: AI Coding Tools > Comparisons I prefer Cursor to Google Antigravity for daily coding. I have used Antigravity, and the supervision it needed on a small task put me off making it my main tool. Cursor and Antigravity both support agent-led work, so my preference comes from the workflow and the changes I had to review, rather than a claim that only one can run agents. Cursor combines an editor with local and cloud agents. Antigravity offers an app, a CLI, and editor integrations as well as its IDE. Both support supervised coding and delegated tasks; [comparing coding tools](/ai-coding-tools/comparisons/) means checking how each handles your code, reviews, and budget. My Cursor use is cloud-agent work against deployed development environments, so I care more about the resulting branch and review path than the polish of the agent chat. Product features and pricing references were checked against official vendor pages on October 17, 2026. That documentation check does not represent a new hands-on test. | Feature | Cursor | Antigravity | | Coding interface | Editor with local agents; cloud agents for delegated work | App, CLI, IDE, and editor extensions | | Background work | Cloud agents and automations on Premium and above | Asynchronous agent tasks alongside interactive coding | | Review workflow | Code diffs and pull-request review | Plans, walkthroughs, artifacts, and code changes | | Model choice | Grok and Composer; third-party models on eligible paid plans | Gemini and selected third-party models | | My preference | My choice for daily coding and reviewing changes | Worth considering if its workflow reduces your correction time | This is one choice within a wider field; our [AI coding tools](/ai-coding-tools/) guide explains the alternatives and our overall preferences. ## Pricing and Usage Limits | Plan | Cursor | Antigravity | | Free access | $0 with limited agent and chat usage | $0 with basic weekly agent limits; unlimited Tab and Command requests | | Individual paid access | Plus $8; Premium $30; Super $100; Ultra $200 per month | Google AI Pro and Ultra raise limits; Google announced Ultra tiers at $100 and $200 per month | | Team access | Teams from $30 per user per month; Enterprise quoted separately | Eligible Gemini Enterprise plans from $30 per seat per month, or consumption pricing with no seat fee | [Cursor's pricing page](https://cursor.com/pricing) limits Plus to Grok and Composer 2.5 models. Premium and higher tiers offer third-party models, but charge for their usage separately at API list prices. A subscription therefore does not include an allowance for every model you can select. [Antigravity's pricing page](https://antigravity.google/pricing) lists free access, higher limits through Google AI Pro and Ultra, and business access through Google Cloud. It does not show a consumer Pro price. The Ultra amounts above come from [Google's May 2026 announcement](https://blog.google/products-and-platforms/products/google-one/google-ai-subscriptions/); confirm the current regional price and allowance at checkout. Antigravity's free plan is a useful starting point if you want to try its workflow without a subscription. Cursor's lower-priced Plus plan is relevant if Grok meets your needs. If you depend on other models or frequent background tasks, compare the applicable allowance and additional usage charges rather than the starting price alone. ## What My Antigravity Test Showed My Antigravity trial exposed a specific problem: I asked for a loading state on a button, and the agent restructured the component folder. The code worked, but the changes went beyond what I requested. I describe that experience in the [Antigravity review](/ai-coding-tools/antigravity/). A fair comparison should measure that correction and review effort, not just whether the output runs. That experience explains my preference for Cursor. It is a specific example of extra review work, not a benchmark proving that Antigravity will make the same mistake on every task. ## Editor and Agent Workflows Cursor suits developers who want to move between manual edits and agent work in a familiar editor. You can inspect files, request a change, review the diff, and check the result without treating every task as a separate handoff. My own use also includes cloud agents, so Cursor's value is not limited to interactive editing. [Antigravity's documentation](https://antigravity.google/docs/overview) describes both interactive pair programming and asynchronous background work. Its app provides a place to manage agent tasks, while its editor integrations let developers keep coding in a supported environment. Plans, walkthroughs, and other artifacts help explain what an agent intends to do and what it produced. For a small bug, compare how quickly you can inspect the relevant code and approve a narrow fix. For a delegated feature, compare how easily you can follow progress and review the completed branch. Either product can support both patterns; the practical difference is how much intervention the workflow needs from you. The [Cursor guide](/ai-coding-tools/cursor/) covers its workflow in more detail. Our [terminal vs IDE agents comparison](/ai-coding-tools/comparisons/terminal-vs-ide-agents/) explains when an editor or a separate agent interface is the better fit. ## Models and Project Context Both Cursor and Antigravity offer model choices, although access and billing differ. Antigravity's documentation includes Gemini and selected third-party models, so treating it as a Gemini-only tool would give readers the wrong comparison. A model's name does not tell you whether the tool supplied the right files or followed your project's conventions. Compare how each workflow handles repository context, project instructions, and test results. A narrow, correct change is more valuable than a persuasive explanation attached to an unrelated rewrite. ## Team Controls and Review Cursor Teams lists centralized billing, usage analytics, privacy settings, and SAML or OIDC single sign-on. More granular access and audit controls sit in its Enterprise tier. Antigravity offers business access under Google Cloud terms, with spending controls and centralized administration on eligible subscriptions. Choose the plan that provides the controls your organization actually requires. Consumer access and business access are different purchasing decisions, even when the interface looks similar. Keep accepted code, tests, and review discussion in your repository and normal tracking system. Set the same permissions for both tools, keep concurrent tasks on separate branches, and retain human approval for sensitive or irreversible changes. An agent's chat history should not be the only record of why a change was made. ## A Practical Trial for Your Team A two-week Cursor and Antigravity trial should measure completed work and human review effort. Pick two to four volunteers, one non-critical repository, and a fixed budget. 1. Give each tool the same bug fix or small feature, acceptance criteria, and permission boundaries. 2. Start separate branches from the same commit. Count any setup or dependency changes as part of that tool's result. 3. Have the same reviewer check correctness, passing tests, unrelated edits, and time spent correcting the agent. 4. Include a failed test and a maintenance task so the comparison covers recovery as well as a successful first attempt. 5. Compare subscription and usage costs with the total time needed to produce a change you would merge. An agent that writes a feature in five minutes but adds an hour of review has not saved the team time. End the trial with a decision about your actual workflow, rather than a claim that one product wins every coding task. ## Cursor vs Antigravity Verdict I would choose Cursor as the primary editor. Its editing and review flow fits my work better, and my Antigravity experience has not persuaded me to switch. Antigravity may suit you if a bounded trial shows that its app or IDE helps you delegate work with fewer corrections. Test the same task in both and count unrelated edits as a cost. My dislike is a first-hand opinion; it does not require your result to match mine. ## FAQ ### Is Antigravity a replacement for Cursor? Antigravity can replace Cursor for some coding workflows, but it is not a drop-in replacement for every editor feature, extension, or integration. Both can work on software tasks, but they organize that work differently. Cursor and Antigravity both support interactive coding and delegated agent tasks. Compare the editor integrations, review process, and plan limits you actually need before replacing a working setup. ### Can I use Cursor and Antigravity on the same repository? Cursor and Antigravity can work on the same repository if the team agrees on the same branch, pull-request, secret-handling, and command-approval rules. Keep work in separate branches, review every generated diff, and avoid running two agents against the same files at once. Store the durable record in the repository because either tool's chat history can disappear. ### Which tool has better models? Neither Cursor nor Antigravity has universally better models. The useful comparison is the models available on your plan and how they perform on your own task. Cursor supports Grok, Composer 2.5 and paid third-party model access, while Antigravity supports Gemini and selected third-party models. Availability depends on your plan and chosen interface. Compare the models available to your plan on one representative task, then score the resulting diff, tests, cost, and review effort. ### How long should a team trial last? Teams comparing Cursor and Antigravity should plan a two-week trial covering ordinary feature work, a failed check, code review, and a maintenance task. Two weeks is often more useful than a single demo because it exposes permissions, handoffs, and cost. Stop early if the tool cannot meet a non-negotiable security or editor requirement. --- ## Cursor vs Claude Code URL: /ai-coding-tools/comparisons/cursor-vs-claude-code/ Cluster: AI Coding Tools > Comparisons Cursor is my first choice for daily editing, while Claude Code is the coding agent I run beside it for repository work and a second opinion. Cursor combines a visual editor, Tab completions, model choice, and local or cloud agents. Claude Code brings Anthropic's agent to the terminal, VS Code, desktop, and web, so this is no longer a simple editor-versus-terminal choice. The useful differences appear after the first prompt: how each tool finds files, handles a mistake, spends your budget, and lets you check work before it lands. | | Cursor | Claude Code | | Form | AI-first editor (VS Code fork) | Coding agent across terminal, IDE, desktop, and web | | You are | Editing directly or delegating an agent task | Directing and reviewing an agent task | | Models | Claude, GPT, Gemini, Grok, Composer | Anthropic's Claude lineup only | | Reads your code | Remote vector index (embeddings) | On-demand grep + file reads, nothing indexed | | Security | Tool permissions and configured sandbox boundaries | Your terminal, your permissions (sandbox optional) | | Work surface | Inline edits and visual diffs | Tasks, command output, and diffs across supported surfaces | These are our two leading picks in the [overall AI coding tools ranking](/ai-coding-tools/); this comparison explains why you might choose one over the other. ## Two Different Philosophies Cursor keeps the agent beside the code. The editor suggests text, accepts selected-code instructions, and shows changes in the same surface where you inspect the files. You can stay close to each edit or hand a larger task to an agent without leaving the editor. Claude Code begins with a task rather than an autocomplete suggestion. You describe an outcome and let it search the repository and propose changes. Its terminal and VS Code sessions can use auto mode to assess tool actions, subject to account and managed settings; other permission modes let you control approvals differently. Claude Desktop provides a visual route into the same coding workflow. In every surface, inspect the final diff and checks. Both products support interactive editing and delegated work. My preference is about where I want to spend my attention: Cursor keeps the files and diffs close, while Claude Code suits a bounded task with repository searches and shell checks. ## Model Experience Gap Both tools can run Claude models, but they do not wrap those models in the same system. Cursor offers Claude beside GPT, Gemini, Grok, and Composer options. Claude Code is built by Anthropic around Claude, its own tools, and its own permission flow. The agent layer decides which files are read, which tools can run, how errors return to the model, and how much prior work stays in context. Those choices can make the same model family behave differently without proving that either product has a universal quality lead. Test the same bounded task in both products before standardising a team workflow. Record the model, changed files, checks, retries, and review time. A named model alone does not predict the quality of the final diff. ### Run a Fair Model Trial Start both runs from the same commit and use the same task brief. Name the allowed paths, forbidden paths, and project checks. Keep the model selection visible in the record, including any automatic routing that chose a model for you. Give each tool access to the same project instructions. Do not give one a polished plan and the other a one-line prompt. If either run needs a hint, record the hint and the point at which the first attempt failed. Compare the changed-file list before reading the explanation. A confident summary can hide an unrelated edit, a skipped test, or a missing migration. The diff and check output are the evidence that can survive a product update. Repeat one failure case as well as one success. Use a task with a known trap, such as a generated file that must not be edited or a test that catches a nearby regression. That shows how each agent responds when the first plan is wrong. ## How They Read Your Codebase [Cursor's privacy documentation](https://docs.cursor.com/account/privacy), checked July 28, 2026, says codebase indexing uploads small code chunks to compute embeddings. It also says the plaintext code stops existing on its servers after the request. Privacy Mode and account policy still need review before indexing client code. Claude Code searches files on demand with tools such as Glob, Grep, and Read. It does not need a separate codebase-index setup before it can inspect a repository. Prompts and selected file content still go to the configured Anthropic service, so "no index" does not mean "no remote processing." The trade is search speed and account policy, not a made-up token multiplier. Check the data path for the plan you use, then test whether on-demand search or an index finds the right files for your repository. ## Security: Power Versus Boundaries Cursor and Claude Code can both edit files and run agent tools, so neither is safe because of its interface. The boundary comes from workspace trust, permissions, sandbox settings, account policy, and the commands you approve. Launch a terminal agent from the intended project, not a broad parent directory. Claude Code's [permission docs](https://code.claude.com/docs/en/permissions), describe allow, ask, and deny rules. Deny rules take priority, and managed settings can set rules that a project cannot override. The `--dangerously-skip-permissions` flag bypasses the normal permission layer. Anthropic says to use it only in an isolated container or VM. The safer option is [sandboxing](https://code.claude.com/docs/en/sandboxing), which sets filesystem and network limits for Bash commands. Sandboxing does not cover every built-in tool, and it can be configured with escape routes. Keep explicit deny rules, review the diff, and separate high-impact actions such as deploys or migrations from an ordinary coding task. ### Verify the Project Boundary Use a harmless trial repository to check the boundary before adding client code. Place a sentinel file outside the project, deny a file inside the project, and ask the agent to list both paths. The expected result should be written before the test begins. Test one blocked command and one blocked network destination. A settings screen is not proof that the rule is enforced. Capture the denial message and the configuration file that produced it, then keep both with the team setup guide. Do not use live credentials as test data. A fake token and an empty account can prove whether the agent reads a path or sends a request. The trial should expose permission behaviour without creating a second incident to investigate. ## Context, Agents, and Integrations Both tools let you store project instructions: Claude Code reads `CLAUDE.md` files and can load skills and subagents under `.claude/`, while Cursor uses project rules under `.cursor/rules`. Treat those files as versioned instructions, then enforce high-risk boundaries with permissions rather than prose alone. Cursor background agents run away from the local editor and can start from product integrations. Claude Code also reaches beyond a local shell through GitHub and other Anthropic surfaces. These remote paths add a second boundary: repository access, service credentials, and the branch or pull request the agent can edit. Cursor keeps visual diffs and editor feedback close to the code. Claude Code can work with browser tools, but browser access is a separate capability with its own account and permission risks. In either product, a generated UI still needs a browser check at the affected viewport. ### Remote Agents Add Another Boundary A remote agent needs repository access before it can create a branch or pull request. Record which GitHub app or service identity has that access, which repositories it can read, and whether it can reach private dependencies during a build. Branch protection still matters when an agent opens the pull request. Require the same checks and review rules used for a human contribution. An agent-created branch should not gain a shorter release path because the vendor reports that its task finished. Check where remote logs and artifacts are stored. A failed build can include file paths, command output, or test data that was never present in the final diff. Retention and deletion rules apply to those artifacts as well as the prompt. ## Speed Versus Thoroughness Cursor can shorten small editor loops with Tab completions, inline edits, and visible diffs. Claude Code can take a bounded task that needs repository search, shell commands, and checks across several files. Response time varies with the model, task, network, and tool calls, so do not treat a demo timer as a stable product fact. > A fast wrong answer costs more time than a slow correct one, so response speed and completion speed are not the same thing. ### Measure Finished Work Split elapsed time into agent time, waiting time, review time, and repair time. Cursor may return an inline edit quickly while a larger Claude Code task runs longer. Neither timer shows how long it took to reach a diff you would merge. Count abandoned attempts alongside successful runs because a fast first response that sends the task down the wrong path can cost more than a slower run that reads the required files first. Keep the failed diff or transcript long enough to explain the retry. Use the same proof command after both runs. If one agent reports a passing check that it did not execute, mark the run unverified. If the check cannot run locally, record the missing service or credential instead of treating the summary as a pass. ## How Much Do Cursor and Claude Code Cost? Cursor Plus costs $8 per month, while Premium costs $30 per month and adds third-party models, cloud agents, Automations, and Grok Bot. Claude Pro costs $20 per month with Claude Code access; Max starts at $100 per month, with a $200 tier for higher usage. These are US monthly subscription prices checked October 16, 2026, before any extra usage. | Plan | Monthly Price | Buying Decision | | Cursor Plus | $8 | Light editor use with its included models | | Cursor Premium / Super / Ultra | $30 / $100 / $200 | Broader model and agent access, with different usage allowances | | Claude Pro / Max | $20 / $100 or $200 | Claude Code with plan-dependent usage limits | | Cursor Teams | From $30 per user | Shared administration and team controls | | Claude Team Standard / Premium | $25 / $125 per seat | Both include Claude Code; Premium raises usage | Check the [Cursor plans](https://cursor.com/pricing) and [Claude plans](https://claude.com/pricing) for annual discounts, seat requirements, and included usage. An API key creates a separate usage bill. Neither subscription promises unlimited use of every model. ### Measure Cost in a Trial Compare a month of normal work, not one polished prompt. Start with one paid plan, record the model or mode for each agent task, and check the account dashboard each week. That turns a credit system into a number you can compare with a subscription. - Use a small, reversible feature that touches several files. - Run the same project checks after each tool's work. - Count review time and retries as part of the cost. - Set a spend alert before leaving any background agent running. Model choice helps when you can name why you switched, such as checking an implementation with a second model. Switching by habit makes the bill harder to explain. A subscription is easier to forecast, though rate limits can still pause a busy session. Neither invoice measures whether the resulting diff was ready to merge. Count review time, retries, and any failed check beside the plan or usage charge. ## How I Use the Pair Cursor is my daily editor and, since September, the plan Grok Bot rides on; Claude Code runs beside it as a coding and coordination surface. The part of my Cursor work with the clearest documented boundary is cloud agents run against deployed dev sites, where the branch, live URL, and browser check give the remote task its edges, and Grok Bot now hands off the delegable background jobs the same way. I do not have a confirmed combined spend to report. That pairing works because the jobs are distinct. Claude Code owns bounded repository work with shell checks. Cursor cloud agents receive tasks tied to a deployed branch and a visible result. The hand-off is explicit, and neither tool's output bypasses review. ## Workflow Fit The work surfaces differ in ways you can test: | Claude Code Fits When | Cursor Fits When | | Repository search and shell checks | Inline diffs and editor review | | Claude-only model path | Several model providers | | Command permissions | Visual feedback | | Task-led terminal work | File-led editor work | You can run Claude Code inside Cursor's terminal, which keeps both surfaces in one window. That arrangement does not merge their billing, permission systems, or remote-agent settings. Review each boundary separately and keep one tool responsible for each task. ## Read the Full Guides For the full picture on each tool, read the [Cursor](/ai-coding-tools/cursor/) and [Claude Code](/ai-coding-tools/claude-code/) guides, which cover setup, workflows, and pricing in far more detail than a head-to-head allows. You can also browse the current matchup set on the [AI coding tool comparisons](/ai-coding-tools/comparisons/) hub to see how these two stack up against the rest of the field. ## Next If you're weighing Cursor against other options, the [Cursor vs GitHub Copilot](/ai-coding-tools/comparisons/cursor-vs-copilot/) comparison is the natural next read. ## Verdict Claude Code is our number-one tool and Cursor our number two. I prefer Cursor when I want an editor, model choice, and Grok Bot on Premium and higher plans. Claude Code also supports background and remote work; that capability is not exclusive to Cursor. Choose Claude Code first if you want an Anthropic-focused agent across desktop, terminal, or web. Our team uses both, with one tool responsible for each task and separate permission and billing checks. Product details and pricing checked against official documentation on October 16, 2026. Personal examples describe the author's original experience. --- ## Cursor vs Codex URL: /ai-coding-tools/comparisons/cursor-vs-codex/ Cluster: AI Coding Tools > Comparisons Cursor vs Codex starts with a tension that appears in almost every AI-assisted coding session. Some work takes shape while you read a file, adjust the request, and watch the diff. Other work becomes useful only after you can state the task, hand it off, and judge what comes back. Cursor grew around the first loop, with the editor as the place where code and conversation meet. Codex grew around the second, with coding tasks available across desktop, terminal, cloud, IDE, and pull-request review surfaces. Both products now reach well beyond those starting points, which makes a feature checklist less useful than [comparing workflows](/ai-coding-tools/comparisons/) from prompt to accepted change. Feature and pricing claims were checked against current Cursor and OpenAI documentation on July 28, 2026. The Comparison Question Where do you want to make the next decision: beside the code while it changes, or at the handoff points around a task, diff, pull request, and test result? For the alternatives beyond this pairing, start with our [guide to the best AI coding tools](/ai-coding-tools/). ## What Are We Comparing? This Cursor and Codex comparison focuses on each product’s coding interfaces and workflows. Model benchmark scores are outside its scope. Cursor and Codex can both inspect repositories, edit several files, run commands, use project instructions, and return work for review. The full [Cursor guide](/ai-coding-tools/cursor/) covers its history, editor tools, models, pricing, and cloud agents. The [Codex guide](/ai-coding-tools/codex/) covers its current surfaces, task writing, review method, and plan access. Agent time and developer time create different costs. An agent may finish a job while you work elsewhere, but the result still creates a review obligation. A fast edit may save minutes inside the editor, yet keep you tied to the session while the request changes. | Question | Cursor Frame | Codex Frame | | Starting point | Editor or Agents Window | Task surface | | Human attention | During the edit | At handoff and review | | Smallest unit | Suggested edit | Bounded task | | Primary evidence | Live diff | Task log and diff | | Remote lane | Cloud agent | Cloud task | ## Cursor's Center of Gravity Cursor is an AI-focused code editor built from the open-source core of Visual Studio Code. Its file tree, tabs, terminal, extension layout, and keyboard habits feel familiar after a move from VS Code. AI sits inside that shell through Tab completion, chat, inline edit, Agent, rules, skills, and model selection, including Cursor's Composer coding model. The editor keeps the work visible while the request is still moving. You can select a function, ask for a small change, reject one part of the diff, run the page, and alter the next prompt after seeing the result. That short loop suits work where each answer creates the next question. Cursor 3 widened the product beyond a single editor pane. Its Agents Window can run several agents across local projects, isolated worktrees, cloud systems, and remote machines. Cloud agents can also work away from the laptop and return pull requests with review artifacts. The editor-first feel remains across that wider product. Tab still helps with the next edit, and a foreground Agent still works beside the open repository. Even the Agents Window can sit next to the editor, which keeps delegated jobs near the place where a developer reads and changes code. ## Codex's Center of Gravity Codex is OpenAI's coding agent, reached through the ChatGPT desktop app, command-line interface, IDE extension, web and cloud tasks, and GitHub review. The standalone Codex desktop app has been folded into ChatGPT desktop, where Codex remains a separate coding experience beside Chat and Work. Its common unit of work is a task with a finish line. You name the result, point to the project, state the boundary, and give the check that should pass. Codex can inspect the repository, edit files, run allowed commands, and bring back output you can inspect. The command-line interface can still be highly interactive. You can ask a codebase question, approve a command, change direction, or review an edit while staying in the terminal. The IDE extension also puts Codex beside the code, so delegation is a product bias rather than a hard product limit. Codex becomes distinct when the task can leave the immediate editing session. Cloud work can run in parallel, the desktop app can keep several chats in view, and GitHub review can examine a pull-request diff against repository guidance. Those surfaces put more weight on task writing and later review. ## Where the Work Happens The same request feels different as it moves through each product. In Cursor, a foreground request begins with the open project and the files around your current position. In Codex, a delegated request often begins with the outcome and the environment it may use. | Work Surface | Cursor | Codex | | Inline coding | Tab and inline edit | IDE extension | | Local agent | Editor Agent | CLI or desktop | | Parallel work | Agents Window | Desktop or cloud | | Remote task | Cloud agent | Cloud task | | Pull-request review | Bugbot | `@codex review` | | Recurring work | Automations | Scheduled tasks | The table shows overlap rather than a set of winners. Each row names a route into similar work, but the surrounding interface changes what you notice. Cursor keeps the file and visual diff close, while Codex gives the task, permissions, log, and returned result more room. ## The Active Editing Loop An active editing loop begins before the request is fully settled. You may know that a form feels wrong without knowing whether the cause sits in markup, styling, state, or validation. Reading the current file and running the page changes what you ask next. Cursor's editor fits that unsettled motion because Tab can propose the next edit without opening a separate task. Inline edit can alter one carefully selected code block at a time. Agent can search the project, edit several files, run a command, and display the changed files for review. The visual diff can expose scope drift when an unexpected file appears. A deleted error path can alter the next prompt. A local preview can show that the code compiles while the page still behaves badly. Codex can join this loop through its IDE extension or command-line interface. Friction and habit create the main difference because both products can do the work. A developer who already works in a terminal may find the Codex session just as immediate as another developer finds Cursor's editor. The deciding test for this loop is interruption cost. Count how often you change the request after reading a file, seeing a diff, or using the feature. Frequent course changes favor a surface that keeps the agent beside the current work. ## The Delegation and Review Loop A delegation loop starts after the work can be described without constant course changes. A good task names the symptom, the expected result, the files or area in scope, forbidden moves, and the command or screen that proves the outcome. Codex gives that handoff several visible homes, including cloud tasks that work away from the local session. ChatGPT desktop can keep parallel coding chats visible. GitHub review can read a pull-request diff, follow applicable `AGENTS.md` rules, and post a standard review after the literal `@codex review` trigger. Cursor also gives delegated jobs their own remote lane. Cloud agents run on remote machines, work on separate branches, and can return a pull request with logs, screenshots, or videos. Automations can start agents from schedules or events from tools such as GitHub, Slack, Linear, and webhooks. The hard part arrives when the jobs finish. Three agents returning three branches do not remove work. They move it from typing into file-list checks, diff reading, test runs, product checks, and overlap decisions. Delegation pays when the task saves more focused time than its review consumes. It fails when a vague request returns a large, plausible diff that nobody can judge quickly. Missing requirements still produce the wrong work. ## Where Cursor and Codex Overlap The overlap has grown enough that old one-line comparisons are unsafe. Cursor reaches beyond autocomplete, while Codex reaches beyond remote jobs. Both products now span local agents, remote work, project instructions, command execution, parallel jobs, and pull-request workflows. | Capability | Cursor Route | Codex Route | | Project instructions | Rules and skills | `AGENTS.md` and skills | | Command execution | Agent terminal | Sandboxed shell | | Remote coding | Cloud agents | Cloud tasks | | Visual proof | Videos and screenshots | Browser and desktop tools | | Review output | Diff or Bugbot comment | Diff or GitHub review | Overlap makes a trial more useful than a long feature inventory. Give both tools the same bounded issue in a repository you know. Compare the files touched, commands run, test result, time spent steering, time spent reviewing, and any extra usage cost. Run a second trial with a task that is still uncertain. Watch which surface helps you discover the requirement and which one encourages a premature handoff. A product can look fast on a fixed benchmark and feel slow in daily work if it fights the way decisions emerge. ## My Workflow I use Cursor cloud agents against auto-deployed development sites, where the agent can inspect a running result after a branch deploys. I give each task a scoped change, the deployed page to inspect, and the check that must pass before I accept the returned diff. I also use Codex in the desktop app for delegated work. In the CodeWalkers promotion flow, we request `@codex review` as a second opinion on the exact pull-request head before a release decision. The [Codex review guide](/ai-code-review/codex/) explains how to request and check a review. The jobs stay separate enough to expose their costs. Cursor returns a deployed page and diff, Codex desktop returns task output, and Codex review returns a GitHub finding that still needs to be checked. I give each task a clear boundary, name the check that will prove it worked, and leave the release decision to a human. A second agent can add another opinion. It cannot turn an unchecked diff into verified work. ## Pricing and Plan Shape Cursor offers Free, Plus at $8 per month, Premium at $30, Super at $100, and Ultra at $200. Teams starts at $30 per user per month. Premium and higher plans add third-party models and cloud workflows; compare their included usage before enabling extra spend. Prices were checked October 16, 2026. Codex is included with eligible ChatGPT plans. Free and Go provide limited Luna access in the desktop app; Plus costs $20 per month, and Pro has $100, $200, and $500 monthly tiers with different allowances. Paid model access and credits vary by plan. API authentication is separately billed and does not unlock every subscription-backed cloud feature. | Cost Layer | Cursor | Codex | | Free entry | Free plan | Limited Luna desktop access on Free/Go | | Paid individual | Plus $8; Premium $30/mo | ChatGPT plan | | Team entry | From $30/user/mo | Workspace plan | | Extra usage | Usage billing | Token credits | | Budget check | Usage dashboard | Usage panel | A useful budget test records the task type beside the spend. Separate inline help, local agent runs, cloud jobs, and pull-request reviews. A total monthly bill without that split cannot tell you which work surface earned its cost. Check the live [Cursor pricing page](https://cursor.com/pricing) and [Codex rate card](https://help.openai.com/en/articles/20001106) before buying or changing seats. Both vendors can alter included usage, credits, and team terms without changing the core workflow described here. ## Safety and Review Burden Both products can read code, edit files, and run commands. Remote agents may also receive repository access, network access, secrets, and a machine that continues without constant approval. That power turns permission setup into part of the task rather than an account detail. Start with the smallest repository scope and network access the job needs. Keep production secrets out of a general coding task. Treat instructions found in issues, web pages, test fixtures, and repository files as untrusted input when an agent can call tools or send data. A careful review begins with the complete changed-file list. Then read the diff, run the named checks yourself, inspect any generated test, and use the changed feature in its final surface. A passing test written from the same wrong assumption as the code can still prove the wrong behaviour. Cursor's live diff can expose drift early because the changed files stay in view during an editor session. Codex's task log and returned diff can make a delegated run inspectable after it finishes. Neither form of evidence is a release approval. The review queue sets the safe amount of parallel work. If you can assess one returned branch well, running five at once produces delay and overlap rather than extra output. Add parallel tasks only after the handoff and review steps remain readable. ## Cursor vs Codex Verdict Cursor is our number-two tool overall, behind Claude Code, and my default recommendation for developers who want a full AI editor. Premium and higher plans also include Grok Bot, with a shared weekly allowance and separate cloud or local access controls. Codex is our close third and a strong first choice when you already like your editor and want desktop delegation, bounded repository tasks, cloud jobs, or pull-request review. Both products cover interactive and delegated work; I would choose by the workflow I use most. For my workflow, the products sit together without doing the same job. Cursor cloud agents check work against deployed development sites, while Codex handles desktop tasks and pull-request review before release. I would pay for both only after each lane saves more focused time than it adds in review and usage cost. ## FAQ ### Can Codex replace Cursor as my editor? Codex can replace parts of your Cursor coding workflow, but its IDE extension is not a complete replacement for the Cursor editor shell. Codex can cover coding work from the terminal, ChatGPT desktop app, IDE extension, or cloud, so any replacement decision depends on the editor features you use each day. ### Can Cursor handle delegated cloud work? Cursor can handle delegated work through cloud agents, and Automations can start jobs from schedules or events. Those tools can return pull requests, logs, screenshots, and videos. This gives Cursor its own delegation path, so compare the handoff, permissions, review queue, and billing with one bounded task. ### Which one is better for code review? Choose Cursor and Bugbot for review tied to your Cursor workflow; choose Codex for its GitHub review workflow. Neither is a universal winner without checking the defects each finds in your repository. Codex can review GitHub pull requests after an @codex review comment or through automatic review, while following AGENTS.md guidance. The useful test is whether either reviewer finds confirmed defects that your normal checks miss. ### Which one is easier for a beginner? Cursor can be easier for a beginner who wants files, edits, and diffs together in a full editor; Codex desktop suits someone who prefers describing a bounded task and reviewing the result. Codex in the ChatGPT desktop app is also visual, but delegated work still asks the learner to judge a returned diff, test result, and task boundary. ### Do Cursor and Codex use the same billing model? Cursor and Codex use different billing models: Cursor sells editor plans with included model usage and optional usage-based spend. Codex is included with eligible ChatGPT plans, then draws from plan limits or credits under OpenAI's current rate card. Team terms differ again, so check both account pages before setting a budget. --- ## Cursor vs GitHub Copilot URL: /ai-coding-tools/comparisons/cursor-vs-copilot/ Cluster: AI Coding Tools > Comparisons I prefer Cursor to GitHub Copilot for multi-file agent work because it needed less correction in my own projects. Copilot remains useful when you want AI inside an existing editor such as VS Code or JetBrains, or your team already manages development through GitHub. Both can search a repository, edit several files, and run development commands; the difference is how well each workflow fits your work. That difference matters most when a task crosses several files. Compare quick edits and planned agent runs, then check price, team controls, and the terminal tools that can sit beside either editor. Updated July 2026: [Grok 4.5 has been available in Cursor since July 8](https://cursor.com/blog/grok-4-5), while [GPT-5.6 Sol, Terra, and Luna began rolling out in GitHub Copilot on July 9](https://github.blog/changelog/2026-07-09-openais-gpt-5-6-sol-terra-and-luna-are-now-available-in-github-copilot/). Availability depends on the Copilot plan and rollout, so check the live model picker before budgeting around a specific variant. This table gives the outline before the details: | | GitHub Copilot | Cursor | | Form | An extension on your existing editor | A whole IDE (VS Code fork) | | Runs in | VS Code, JetBrains, and more | Editor, CLI, and cloud agents | | Agent mode | Multi-file agent editing; more correction in my trial | Agent with a choice of models, including Composer | | Entry price | Pro $10/mo | Plus $8; Premium $30/mo | | Best at | Paid completions, editor choice, GitHub integration | Reliable agent edits, Plan mode, speed | Our [broader coding tools comparison](/ai-coding-tools/) puts this choice in context alongside Claude Code, Codex, and OpenCode. ## How They Actually Work Copilot installs as an extension, you sign in, and it adds AI features to your existing editor as a modular panel you can turn on and off. Cursor is a fork of VS Code, so it literally looks the same, but the entire interface is designed around AI-assisted editing. The reason I didn't get Cursor the first time is that I compared the features that look identical: tab completions, chat windows, and which models I could pick. Those surface features aren't where Cursor pulls ahead. The gap shows up in agent reliability, in Plan mode, in Composer's speed, and in the way a whole feature flows from natural language to a committed PR. None of that surfaces in a quick trial where you just ask the AI to explain some code. VS Code itself is a strong editor, and its own agent mode reached general availability in early 2026, so the real gap here is the AI layer, not the editor. A couple of us on the team run the agent extensions inside VS Code for exactly that reason. ## Context and Chat Both tools offer a chat sidebar where you ask questions about your code, and they feel pretty different once you live in them. Copilot uses @ symbols for context (@workspace, @file, @terminal), slash commands like /explain and /fix, and inline chat. Cursor is context-aware by default, references work similarly with @, and it can edit code directly from the chat window. When you ask Cursor a question, it often already knows what you're looking at, where Copilot more often needs you to specify context by hand. Neither is dramatically better for a simple one-off question, but Cursor's edge becomes obvious when the work spans multiple files. ## Agent Mode This is the big one for most developers. I used Copilot's agent mode extensively before switching, and the same problems kept biting me: code placed in the wrong file, implementations that left the logic half-finished, and poor recovery when it made a mistake. For simple single-file tasks it's perfectly fine, but anything coordinating changes across the API, the UI, and the tests required constant babysitting. Cursor's Composer is a coding model used by its agent, rather than the name for every agent feature. Fast responses made me more willing to use it for small changes, but a fixed completion time would overstate what the tool guarantees. The stronger evidence for my preference is the five-file task below: I spent less time repairing the result. The example I keep coming back to is this one. I needed an endpoint that created a route, added a service function, updated the types, added a frontend piece, and added tests. In Copilot this took multiple frustrating attempts, and I spent more time fixing its mistakes than I saved. In Cursor I described the feature, reviewed the plan, and let it run across five files; most worked on the first pass, and the total was about 10 minutes including review. The verdict: Cursor wins decisively for multi-file agent work. If agent mode is central to how you work, this alone might justify the switch. ## Plan Mode Plan mode is one of Cursor's most important differentiators. Before it changes anything, you see which files it will touch, what changes it will make, why, and in what order. You can pull a file out of scope, ask it to reconsider an approach, or hand it the context it's clearly missing before any code moves. Reviewing a plan lets me catch a wrong approach before the files change. Copilot also supports planning in VS Code and other supported surfaces, so planning alone is no longer a reason to switch. I still prefer Cursor's flow because of the results I got when the task moved from a plan to coordinated edits. ## Code Review Both tools offer code review, and my experience with both has been mixed. Enable Copilot automatic reviews in GitHub settings; a `.github/copilot-instructions.md` file supplies review guidance, rather than enabling the feature. I found its comments noisy enough to turn it off. Cursor's Bugbot caught more useful issues in my experience. Bugbot access is included on current Premium and higher plans, with usage limits and billing conditions. Neither reviewer removes the need to check a finding against the code. ## Pricing Pricing is where developers get surprised, and where Cursor has taken heat. As of June 1, 2026, Copilot meters model usage in GitHub AI Credits (1 credit = $0.01), replacing the old premium-requests system. Code completions and Next Edit suggestions never consume credits and stay unlimited on paid plans, capped at 2,000/month on Free. Credits cover chat, agents, the CLI, and code review, metered by each model's token rate, so an Opus-class model in agent mode can eat a meaningful chunk of your allotment fast. | Plan | Copilot | Cursor | | Free | $0, 2,000 completions/month | Free $0, limited usage | | Entry paid | Pro $10/month, 1,500 credits | Plus $8/month; Premium $30/month for third-party models and cloud agents | | Power | Pro+ $39/month, 7,000 credits | Super $100/month, higher usage allowance | | Heavy | Max $100/month, 20,000 credits | Ultra $200/month, highest individual allowance | | Team | Business $19/user/mo, Enterprise $39/user/mo | Teams from $30/user/mo | Cursor frames its plans around included model usage plus optional on-demand usage billed in arrears, with the dashboard as the source of truth for how fast you're burning it. Both tools make it easy to blow through your allocation once your workflow turns agent-heavy. The June 2025 pricing revision is a historical warning about assuming a plan will keep the same limits forever. Cursor changed its pricing and later acknowledged confusion about usage charges. The practical lesson holds for both tools: check the current allowance, set an extra-spend cap where available, and review the usage dashboard regularly. My own receipts make the same point: > Early on I enabled usage-based pricing without a limit, told myself I'd be careful, and I absolutely wasn't. I burned through $60 in Cursor credits and $40 in Codex credits within three days. Now I never enable overages without a cap, and I monitor usage weekly. At the time of that trial, my setup included a $40 Cursor Team seat, $200 Claude Max, and a Bugbot budget based on PR volume. Those are historical receipts, not current Cursor plan prices. Today I would compare Premium at $30 with the higher tiers before paying for extra usage. My reason for choosing Cursor remains the work it saves me, rather than the lowest subscription fee. ## The Sustainability Question My concern is whether a tool remains useful if its price, included models, or allowances change. I use Cursor because it fits my work today, but I would not build a budget around its pricing staying fixed. The same caution applies to Copilot. Keep project instructions, tests, and the source code in the repository. That makes a future move less dependent on an editor's chat history. Review the bill alongside completed work each month, rather than treating either vendor's funding or market position as a promise about future prices. ## Background Agents and Integrations Cursor's newer releases lean hard into parallel work. Cursor's Agents Window can run many agents in parallel across local repositories, worktrees, cloud environments, and remote SSH, so one can refactor while another writes the tests. The catch is that parallel agents multiply both spend and review load, and they can alter your privacy and billing assumptions, so check the current cloud-agent policy before enabling them. Copilot's cloud agent can work autonomously on GitHub issues and open PRs, and its CLI sessions run in the background steered through VS Code and GitHub Mobile. On external connections Cursor is ahead, with direct Slack and Linear links that turn a tagged thread or assigned ticket into a cloud agent and a finished PR. Copilot's strength is the GitHub-native experience, and it works with MCP servers too. ## Enterprise Copilot suits teams that want GitHub administration and AI inside their existing editors. Cursor suits teams willing to adopt its editor and agent workflows. Compare the actual plan's identity controls, data handling, audit facilities, and contract terms before choosing. Cursor Privacy Mode restricts model training and data handling under its documented policy; it is not a blanket promise that no service ever retains any data. Copilot content exclusions also have limits. They do not apply to every agent surface. The [Copilot enterprise setup guide](/ai-coding-tools/copilot/setup-and-enterprise/) explains those boundaries. A product label alone does not settle the enterprise decision. ## The Terminal Angle Cursor Premium and higher plans include Grok Bot alongside the editor. Its persistent cloud computer can carry tools and work between tasks; optional local access has separate permission controls. Weekly usage is capped. Copilot offers its own cloud agent and CLI workflows, so delegation is available on both sides. I still run [Claude Code](/ai-coding-tools/claude-code/) beside Cursor for repository work and a second opinion. That pairing is the setup I recommend from my own experience. ## Which Should You Pick? Cursor is our pick over GitHub Copilot for developers who prioritize multi-file agent editing, plan mode, parallel agents, and Grok Bot on Premium or higher. Claude Code remains our number-one AI coding tool overall. Choose GitHub Copilot only if one of two constraints decides it for you: - Your organization has approved Copilot under its existing contract and requires that managed setup - Your organization is GitHub-administered, will not change editors, and your usage is light and mostly chat rather than agent edits The cheap-entry argument ($10 Pro) is weaker than it looks: Cursor's Free tier is free to trial, and light chat usage is served fine by tools you may already pay for. Copilot Pro's monthly credits cover a lot of day-to-day work, and completions stay free; Cursor is where the agent reliability and speed earn the higher price, which is why I switched. Run both, or lean on a terminal tool instead, if you already use Claude Code heavily, if different projects pull you in different directions, or if you're still testing which workflow fits before committing real money. I switched because agent mode reliability mattered more to me than the price difference, and my productivity noticeably rose once I could trust the AI with multi-file changes without babysitting. The right move is to run both on real work for a couple of weeks, push them with tasks that span several files, and let that tell you which one fits how you actually code. ## Read the Full Guides For the full guides, read the [Cursor](/ai-coding-tools/cursor/) and [GitHub Copilot](/ai-coding-tools/copilot/) hubs, which cover each tool on its own terms, and browse the [AI coding tool comparisons](/ai-coding-tools/comparisons/) hub for the other head-to-heads in this cluster. ## Next If you're weighing Cursor against an open-source agent instead, read [Cursor vs OpenCode](/ai-coding-tools/comparisons/cursor-vs-opencode/). Product details and pricing checked against official documentation on October 16, 2026. Personal examples describe the author's original experience. --- ## Cursor vs OpenCode: What We're Actually Comparing in 2026 URL: /ai-coding-tools/comparisons/cursor-vs-opencode/ Cluster: AI Coding Tools > Comparisons I prefer Cursor when I want an editor and agent features that work together with little setup. OpenCode is the stronger fit when control over the agent code, model provider, or local inference matters more. Mark Gates uses OpenCode daily and makes that case below; our disagreement is part of this comparison. Cursor is a proprietary editor and agent platform with free and paid plans. OpenCode is an open-source agent you can run from a terminal, desktop app, or supported editor. Its client is free, while hosted inference, hardware, and optional subscriptions can still cost money. When [comparing AI coding tools](/ai-coding-tools/comparisons/), feature counts alone hide three different choices: 1. Whole products: Cursor's IDE, Composer 2.5, Cloud Agents, BugBot and automations against OpenCode as a terminal, desktop, or extension agent. 2. Agent layers only: context, self-correction, plan-mode safety, MCP and skills, with the IDE taken out. 3. Money: paid Cursor against free VS Code plus OpenCode plus the models you bring. Agent behavior and total cost matter more than the number of features bundled with the editor. Mark Gates, who uses OpenCode daily, explains his preference below. Terms Used Here The agent layer means the code around the model: the part that feeds it your files, runs its commands, manages context, and loops on errors. A model is just the weights on their own. A token is the chunk of text a model reads and bills you for. LSP is the Language Server Protocol, the thing that gives your editor its red squiggles. A git worktree is a second checkout of the same repository in its own folder. Version scope: the configuration and permission examples here describe OpenCode V1. OpenCode V2 changes the runtime, configuration names, and defaults; follow the V2 migration guide for a V2 installation rather than copying V1 rules unchanged. The [main AI coding tools guide](/ai-coding-tools/) gives our overall recommendations. ## What is Cursor? Cursor is a fork of VS Code with AI in the editing surface and a growing stack of paid features on top: sidebar chat, inline edits, and Composer for multi-file work. The VS Code base is open, and Cursor's own product is not. | Cursor at a glance | | | Type | Proprietary VS Code fork | | Model | Composer 2.5; Claude, GPT, Gemini, Grok | | Headline features | Chat, inline diffs, Cloud Agents, BugBot | | Enterprise | SOC 2 Type II, SSO, SCIM | | Account | Required | | Price | Free; Plus $8/mo; Premium $30/mo; Teams from $30/user/mo | ![image]() Cursor's home leans agent-first now, with New Agent and Automations sitting beside your workspaces. ## What is OpenCode? OpenCode is a free, open-source (MIT) AI coding agent, written in TypeScript and built by Anomaly. It runs in the terminal, a desktop app, or as an extension inside VS Code, Cursor, Windsurf (now Devin Desktop), and VSCodium. You bring your own model, and it stores no code or context on its own service by default. Session data stays on your machine, and `/share` sends a shared conversation to OpenCode's hosting service. It runs as a client and a server, so sessions can be remote, parallel, or scripted headless. | OpenCode at a glance | | | Type | Open-source coding agent (MIT) | | Model | None; 75+ providers, plus local | | Headline features | LSP loop, MCP, agents, client/server | | Surfaces | Terminal, desktop (beta), IDE extension | | Account | Not required for the tool | | Scale | 185K+ stars, 900 contributors (July 2026) | ![image]() OpenCode on its own: a terminal agent you point at any model, here running Big Pickle through Zen. ## How do Cursor and OpenCode differ? Cursor combines an editor with integrated agent features, while OpenCode provides an open-source agent that you can connect to your chosen model and use in a terminal or supported editor. The main tradeoffs are setup, model access, cost, and control over the agent itself. | Question | What it really asks | | Agent quality | Does model output become correct edits? | | Extra context | Cursor indexing vs OpenCode's LSP loop | | Safety | Does it run commands without asking? | | Models | Composer 2.5 vs newer open models | | Cost | Paying for models or polish? | | Enterprise | Turnkey controls vs self-hosted, custom | One more frame runs underneath all of them: OpenCode inside a free VS Code against OpenCode inside a paid Cursor. Plenty of developers do exactly that. ## Is Cursor or OpenCode easier to set up? I find Cursor easier to set up for a first coding session because its editor and agent features come together. OpenCode gives developers more configuration choices, including their model provider and optional local inference. Your existing workflow affects which setup feels easier: - Already in VS Code: Cursor is close to a free switch, because extensions, keybindings, and muscle memory carry over. - Already in the terminal with Claude Code or Codex: OpenCode is the smaller jump, because it works the way you already work. - New to both: OpenCode's setup friction is real, and Cursor removes it. ### Running OpenCode in an Editor You do not search the marketplace for it. Open the built-in terminal in VS Code or Cursor, run `opencode`, and the official extension installs itself. Launch it later with `Cmd+Esc` on Mac or `Ctrl+Esc` elsewhere. That auto-install path is also the safe one because the official extension comes from Anomaly, and the marketplace lists unofficial lookalikes beside it. | Setup | Cursor | OpenCode | | Time to first result | ~2 minutes | Longer; provider and keys first | | Steps | Download, sign in, type | Install CLI, choose model, run | | Account needed | Yes | No (unless you use hosted Zen) | | Best for | Beginners, fast start | Developers who want control | ## Plans and Pricing Cursor uses fixed tiers with a usage pool. OpenCode is free, and you pay only for the models you run, or no per-token provider charge on local hardware. The economic question follows straight from there. If you are happy in VS Code, the free path is VS Code plus OpenCode plus the tokens you use, and Cursor has to justify a subscription on top of an editor you could run for nothing. For many people Composer 2.5 and the bundled agents do that. There is a catch on the metered side. I once left an agent running overnight on premium models and found about sixty dollars gone before I checked the dashboard. ### Cursor's Plans | Cursor plan | Price | What you get | | Free | Free | Limited usage | | Plus / Premium | $8 / $30 per month | Premium adds third-party models, Cloud Agents, Bugbot, and Grok Bot | | Super / Ultra | $100/mo / $200/mo | Higher included usage allowances | | Teams | from $30/user/mo | SSO, admin, analytics | | Enterprise | Custom | Full compliance stack | ### How much does OpenCode cost? The OpenCode client is free. Paid hosted models and optional subscriptions cost extra; some hosted models are free. Local inference requires your own hardware and electricity. | OpenCode cost | Price | Notes | | The tool | Free (MIT) | Open source | | Bring your own model | Provider usage, or local hardware and electricity | Any provider, OpenRouter, or Ollama | | Zen gateway (optional) | Pay-as-you-go, plus free models | Selected tested models; some free | | Go subscription (optional) | Go $10/mo; Go Plus $40/mo | Usage limits; optional top-ups | Zen also offers models marked Free in its current picker. Availability and terms change, so use that live list before choosing a model for ongoing work. The screenshot below records the options available during our original run. The Privacy Trade Free and paid models can have different retention and training policies. Check the specific model and provider terms before sending confidential code; a free price does not define the data policy. OpenCode shows a running total in the terminal: tokens used and dollars spent, updating with each message. After that night, watching the meter tick changed how aware I am of what a long run costs. ![image]() OpenCode's model menu, where the Zen free tier shows up as models marked Free, like DeepSeek V4 Flash and Big Pickle. ### Zen, Go, and OpenRouter The four ways to feed models into OpenCode are easy to confuse, so here they are plainly: - [Zen](https://opencode.ai/docs/zen/): OpenCode's own gateway, a selected set the team tested and tuned, pay-as-you-go, free models included. - [Go](https://opencode.ai/go): a low-cost subscription with model-specific usage limits and optional credit top-ups. - [OpenRouter](https://openrouter.ai/models): one key, hundreds of models. OpenCode's docs note you cannot be sure which provider or version you land on, the exact problem Zen was built to fix. - Direct or local: skip all three, plug in a provider key, or run on your own hardware. | Model access | What it is | Billing | | Zen | OpenCode's selected, tested gateway | Pay-as-you-go, plus free models | | Go | Subscription with usage limits | Go $10/mo; Go Plus $40/mo | | OpenRouter | Third-party gateway, hundreds of models | Pay-as-you-go; you pick the routing | | Direct or local | Your own provider key, or Ollama | Provider's rate, or $0 local | Zen and Go both give real access to strong current models, so this holds up for real work rather than being a free-tier demo. ![image]() From one of our OpenCode runs: the live tally of tokens used and dollars spent, updating as the agent works. ## Models and Performance Composer 2.5 is Cursor's own model, and its lineage sits at the centre of the agent-layer debate. Per Cursor's blog it is built on Moonshot's open Kimi K2.5 checkpoint, with roughly 85% of compute spent on Cursor's own post-training and reinforcement learning. Two fair readings sit on top of the same facts: - Cursor's case: 85% of the work is its own RL, and on its agentic benchmarks Composer ties frontier models at a fraction of the cost. - The OpenCode case: the base is an open model the frontier has passed, since Kimi K2.6 and Qwen3-Coder post-date K2.5, and Composer runs only on Cursor's own surfaces rather than any provider you choose. Whether a newer open model inside OpenCode's agent layer beats Composer 2.5 inside Cursor's is what serious users are testing. On These Benchmark Numbers The benchmark numbers below are vendor-reported, with no independent reproduction on a shared scaffold at launch. | Models | Composer 2.5 (Cursor) | Open models (OpenCode) | | Base | Moonshot Kimi K2.5 plus Cursor RL | Any current model you choose | | SWE-Bench Multilingual | 79.8% (vendor-reported, near Opus 4.8) | Depends on the model | | Token cost | About a tenth of Opus (Cursor) | Varies; open models undercut it | | Where it runs | Cursor app, web, CLI, mobile, SDK | Anywhere, including fully local | | Ceiling | Fixed to Composer's release cycle | Newest open model on release | ### Head-to-Head Runs I recorded two of these. The first pits Composer 2.5 against GLM 5.1 through Zen, and Composer wins on speed by a wide margin. ![image]() Composer 2.5 in Cursor against GLM 5.1 through OpenCode. Composer is faster here, though GLM 5.1 is a reasoning model tuned for correctness over raw speed. That is the wrong fight to read into it. GLM 5.1 thinks before it answers, so it was never going to feel fast in an interactive loop. The second run is fairer, Composer 2.5 against Big Pickle, one of Zen's fast models. ![image]() Composer 2.5 against Big Pickle, one of Zen's fast free models. Big Pickle is quicker, but the chart it makes needs an editor to render. Big Pickle is quicker, and could not render a chart I could read in the terminal without extra setup. That is the surface as much as the model. Match the model class and the serving, and the gap narrows. Both field a wide lineup beyond Composer, and it changes fast enough that any list here goes stale: - In Cursor: Composer 2.5 for the agent, Fusion behind Tab autocomplete, plus Claude, GPT, Gemini and Grok. Current context windows live on [Cursor's models page](https://cursor.com/docs/models). - In OpenCode: whatever you like, via [Zen's selected list](https://opencode.ai/docs/zen/), the [full provider list](https://opencode.ai/docs/providers/), or [OpenRouter](https://openrouter.ai/models) for the long tail. Cursor curates a tested shortlist; OpenCode hands you the whole field. For current names and prices, use those source pages rather than any list on this one. ![image]() Cursor's model picker, with Composer 2.5 alongside the frontier names and an Auto router plus Max Mode. ## The Agent Layer: Context, Safety, and Control This is the technical heart of the comparison, and where the IDE drops out. Weights are half of what you experience; the agent layer decides how context is managed, how tools are called, and how the thing recovers from its own errors. That is why one model posts different results in different harnesses. ### Context and Memory The two find and hold code differently, and it shows up in the failure modes rather than the feature list: - Cursor builds an embeddings index to locate files fast, and reviews edits through an inline diff in the file you are already in. - OpenCode runs a language-server loop that checks the edit against compiler diagnostics before reporting back, catching its own mistakes during the task. - Room to work: Composer 2.5 runs a 200K window with self-summarisation; some models support larger contexts, with model-specific limits and usage costs. - OpenCode takes whatever window your model has and compacts the conversation automatically near the limit. | Context | Cursor | OpenCode | | Finding code | Embeddings index of the repo | Reads files on demand | | Self-correction | Inline diffs you review before accepting | LSP feeds compiler errors mid-task | | Context window | Model-specific limits; Composer 2.5 up to 200K | Your selected model's context window | | Long sessions | Self-summarization plus compaction | Auto-compaction, plus manual `/compact` | ### Plan Mode, Auto Mode, and Command Safety Both have a plan mode and configurable command controls. Their defaults point in opposite directions, and that is the part to understand before either one touches production code. - Cursor ships cautious: allowlist first, then a sandbox with no internet access, then an LLM classifier that allows or blocks the call. The docs are blunt that all four run modes are best-effort. - OpenCode ships permissive: most operations run without asking, and you tighten it yourself with precise rules like `"git push *": "deny"`. Before You Point It at Production Neither default is wrong, but a regulated team should know OpenCode needs configuring before it is safe to let loose. | Safety | Cursor | OpenCode | | Plan mode | Shift+Tab; reviewable plan first | Built-in read-only Plan agent | | Command control | Allowlist, sandbox, then a classifier (`permissions.json`) | `allow`, `ask`, `deny` globs | | Default posture | Safer by default (allowlist plus sandbox) | Allows most until you configure | | Stated limit | Docs say "best-effort"; bypasses possible | You own it; `doom_loop` asks | ### MCP, Skills, and Extensions Both speak MCP and work with skills, so the difference is reach. Cursor inherits the VS Code extension library and adds Hooks that block commands or redact secrets; OpenCode is open source, so the agent code itself is forkable. | Extensibility | Cursor | OpenCode | | MCP | Yes | Yes | | Skills and rules | Skills, rules, Hooks (block, redact) | Skills, custom and forkable agents | | Ecosystem | VS Code extension library | Open source; fork the agent code | ## Modes and Agents Both give you more than one way to run, and the sets line up only loosely. - Cursor's four modes, each starting its own context: Agent makes changes, Ask answers without touching files, Plan writes a reviewable plan, and Debug instruments your app with runtime logs. - Cursor's `/multitask` is a command rather than a fifth mode, fanning a request out to async subagents. Since 3.0 all of it lives in the Agents Window, each tab holding its own mode, model, and workspace. - OpenCode's two primary agents: Build with every tool enabled, Plan locked down so edits and commands need approval. Tab switches between them. - OpenCode's three subagents, called by @ mention: General for parallel multi-step work, Explore for read-only codebase search, Scout for external docs and dependencies. - Custom agents with their own prompts, models, and permissions are how you would rebuild something like Debug mode yourself. The split is the one running through the whole comparison. Cursor's modes are fixed, named, and ready, with Debug as a real extra. OpenCode's are a smaller built-in set you extend. | Mode | Cursor | OpenCode | | Do the work | Agent | Build (default, all tools) | | Answer only, no edits | Ask | Explore subagent (read-only) | | Plan before coding | Plan | Plan (edits and commands ask) | | Reproduce and fix bugs | Debug (instruments your app) | No built-in; build a custom agent | | Run parts in parallel | `/multitask` async subagents | General subagent and parallel sessions | | Research external docs | Not a mode | Scout subagent | | Make your own | Custom modes | Custom agents | ![image]() Cursor's mode switch: Agent, Plan, Debug, Multitask, and Ask, each starting its own context. ## Extensions Cursor is a VS Code fork, so most extensions carry over. The marketplace is what trips people up: Cursor pulls from the Open VSX Registry rather than Microsoft's, and Microsoft's proprietary extensions are not published there. - Missing: Pylance, the C# Dev Kit, the C and C++ tools, Remote-SSH, and Live Share. Microsoft also blocks some from activating in a fork at all. - Workarounds: maintained forks under the Anysphere publisher name, or sideloading a VSIX. Both work, and both are fiddly. - If you lean on first-party extensions, plain VS Code is the smoother seat, and running OpenCode inside it sidesteps the issue. | Extensions | Cursor | VS Code | | Registry | Open VSX (most, not all) | Microsoft Marketplace (full) | | Microsoft first-party | Pylance, C#, C/C++, Remote-SSH often blocked | All supported | | Workaround | Anysphere forks or sideload a VSIX | None needed | ## Privacy, Local Models, and Offline This is where "can I keep my code off someone else's servers" gets a real answer, and the primary docs are clearer than the marketing. OpenCode can connect directly to a local model. A custom model endpoint in Cursor still has a different network path. From Cursor's Data-Use Page It states that even if you use your own API key, your requests still go through Cursor's backend, because that is where it does its final prompt building. Your code runs locally, but the AI request does not stay local. - OpenCode talks to Ollama, LM Studio, or llama.cpp directly, with no tunnel and no vendor backend in the path. - Cursor needs an "Override Base URL" plus a public HTTPS tunnel, because its backend is sandboxed and cannot see localhost. - Tab autocomplete stays on Cursor's built-in models even when a custom endpoint supplies the chat model. - Strong open coding models run locally today, so the limit is the agent layer around them, not the weights. | Privacy | Cursor | OpenCode | | Local models | Base URL override plus a tunnel | Native (Ollama, LM Studio, llama.cpp) | | Truly offline / air-gapped | No | Possible after configuring a fully local stack | | Where requests go | Cursor's backend, even with your key | Direct to your model, or local | | Data storage | Privacy Mode; Cloud Agents store code | None on its service by default | For most teams Cursor's Privacy Mode and zero-retention agreements are enough. For an air-gapped or regulated shop, "the request leaves the building" is the line that decides it. The full setup, including hardware, context settings, which open models are worth running, and Cursor's three catches, is in [OpenCode local models with Ollama](/ai-coding-tools/opencode/local-models/). ## Managing Multiple Agents The twist in 2026 is that the more work moves to autonomous agents, the less the editor matters and the more the question becomes how you run a fleet of them. This is where Cursor turned a weakness into a strength. Cursor 3 shipped an Agents Window in April 2026 that runs many agents in parallel across local repositories, worktrees, cloud environments, and remote SSH. OpenCode comes at it from the other direction, as client and server you can script, attach to remotely, and extend with orchestration such as Oh My OpenCode. | Many agents at once | Cursor 3 | OpenCode | | Interface | Agents Window: one sidebar, agent tabs | Terminal sessions; OMO for orchestration | | Parallelism | Many agents; worktrees optional | Many sessions; you manage isolation | | Where it runs | Local or cloud, in one view | Local or server; any client | | Best for | A managed board, built in | A scripted pipeline you control | This is where I think Cursor's direction is winning. A power user will build something more flexible on OpenCode, and most people will want the board that already exists. The OpenCode side, including worktree isolation, remote clients, and headless scripting, is in [running many OpenCode sessions at once](/ai-coding-tools/opencode/session-fleets/). ## Enterprise Cursor Enterprise offers the procurement and administration stack large organisations expect. OpenCode runs inside your own infrastructure with your own gateway, and it now sells a per-seat Enterprise product too, though its public Enterprise page does not state SOC 2 certification. | Enterprise | Cursor | OpenCode | | Compliance | SOC 2 Type II, GDPR/CCPA, published | No stated SOC 2; custom validation | | Identity and admin | SAML/OIDC SSO, SCIM, audit logs, allowlists | SSO integration; org config endpoint | | Deployment | Cloud only | Inside your own infrastructure | | Adoption | Cursor's figure: 64% of Fortune 500 | Self-hosted, open, auditable | If you need published audit evidence to hand a procurement team today, Cursor has it. If your hard requirement is that code never leaves your perimeter, OpenCode is often the only realistic answer. The deployment detail, the outbound-path map, and what its Enterprise product does and does not publish are in [OpenCode for regulated and enterprise teams](/ai-coding-tools/opencode/enterprise/). ## When should you choose Cursor? I recommend Cursor for developers who want an integrated editor, quick visual review, and less setup. I still pay for Cursor and open it every day, so that is where my recommendation starts. The inline diff lets me approve or reject a change in the file I'm already looking at, which is the quickest way I've found to review changes. The bundled pieces save me steps I would otherwise wire up by hand, and after the night I watched sixty dollars evaporate, the included usage helps me budget, provided I cap any optional overage. I'd reach for Cursor if you are: - A beginner: one install and a model chosen for you, so your attention goes to the work rather than the plumbing. - A heavy reviewer: the inline diff beats scrolling back through a terminal. - Already productive and fine with the cost: switching to save an affordable subscription is a poor trade against your own time. That is not a claim that Cursor wins for everyone. Mark Gates makes OpenCode's case below and it is a strong one. For me, if you want one coherent place that works on day one, Cursor is still the easy yes. ## When should you choose OpenCode? OpenCode is the stronger choice when control over model providers, local inference, or the agent's source code matters more than a bundled editor experience. I don't live in OpenCode the way Mark Gates does, so I asked him to make this case in his own words. ![image]() [Mark Gates](/about/mark-gates/) > Start with my bias: I don't like renting my tools. If you are not paying Cursor specifically for Composer 2.5 and the bundled features, you are mostly paying for a VS Code fork you could run for free. OpenCode flips that around. I bring my own model and can switch it mid-task, I get a stronger open model the day it ships, and the agent code is open enough to read and fork when something annoys me. I try not to mistake a preference for a trend, so it matters that I'm not alone. "Terminal Is All You Need" described OpenCode clearing a lint problem across twelve files in about forty-five seconds by writing its own sed commands and re-running the linter to check itself. When it reached the top of Hacker News with over a thousand points, the loudest reaction was relief at having a real open-source option. Nobody wants to be one pricing revision away from rebuilding a workflow. I will not pretend it is frictionless: - The fast release cadence breaks things from time to time. - It can be heavier on memory than a lean CLI agent. - Because V1 allows most operations by default, you have to set up your own guardrails before you point it at anything that matters. That trade is worth making once you understand the agent layer. If you don't yet, waiting is a reasonable call. I'd choose OpenCode if you are: - An intermediate or senior developer: model choice and open agent code matter more to you than a polished default. - In a regulated industry or government: a local model keeps code inside your perimeter, where Cursor routes requests through its own backend even with your key. - Cost-conscious, or chasing the newest models: you pay the provider for usage, or supply your own local hardware, and you run a better open model the day it drops. One update to my own setup: I have moved off VS Code entirely, and OpenCode runs in my terminal against open models, locally where I can. I have also found a use for Grok Bot that I did not expect. Its VM runs Linux, so I have Grok Bot run OpenCode there as a sandbox. Agents I don't yet trust with my own machine work on an isolated virtual machine instead, and if one makes a mess, the mess stays there. It is not my main workflow, and it has real limits: the usage cap is tight, and I keep this workflow in its cloud computer rather than granting local-machine access. None of that changes my case here: the agent layer and the model are still where your independence lives, and OpenCode is still how I keep them mine. ## What Developers Say Public 2026 discussion shows a consistent split, and it is most useful for spotting which trade-off bites which person. | Pattern | What developers report | | Run both | Cursor for editing, OpenCode for models | | Loved vs used | Terminal agents loved; Cursor used daily | | Lock-in | Model choice drives most switches | | Real cons | Regressions, memory use, permissive defaults | ## Who should choose Cursor or OpenCode? I recommend Cursor for beginners and developers who value integrated editing and visual review. OpenCode better suits developers who prioritize an open agent, provider choice, or local models and are comfortable configuring their workflow. | You are… | Start with | Why | | Beginner | Cursor | One install, visual review | | VS Code user | Cursor | A fork; extensions carry over | | Solo dev | Cursor | Included usage with extra-spend controls | | Intermediate or senior dev | OpenCode (in an IDE) | Model choice, open code, no lock-in | | Terminal-native, new to IDEs | OpenCode | Smaller jump than a new editor | | Enterprise | Cursor | Published SOC 2, SSO, SCIM, logs | | Regulated industry | OpenCode | Local inference can keep code inside | | Government / air-gapped | OpenCode | Can run offline with a verified local stack | | Cost-conscious at scale | OpenCode | Free agent, cheap or local models | | Running many agents at once | Cursor | Agents Window manages a parallel fleet | | Stuck on VS Code and Copilot | OpenCode | Works without replacing VS Code | ## The Verdict Cursor has become a much richer product than a year ago, and OpenCode the most popular open-source coding agent. They solve overlapping but different problems, so a single winner would miss the point. | Choose Cursor if… | Choose OpenCode if… | | You want day-one results | You want the agent layer | | Fast visual review matters most | Local models inside your perimeter | | You need published enterprise controls now | You want the newest open model | | You want a managed agent board | You want a scriptable pipeline | ### Can you use Cursor and OpenCode together? You can use Cursor and OpenCode together by running OpenCode in Cursor's integrated terminal while using Cursor to read, edit, and review files. Keep concurrent agent tasks separate so they do not overwrite each other's changes. Keep the Cursor subscription for the polish and point OpenCode at cheaper or newer models. Or drop it, run OpenCode inside free VS Code, and pay only for the models you use. ### Where I Land A lot of the developers who tell me they love OpenCode are still on VS Code and have never tried Cursor, often because their company runs VS Code and Copilot and no other subscription gets approved. The appeal makes complete sense there, since OpenCode is a real upgrade over Copilot alone. If that is you, try Composer 2.5 first before calling OpenCode the clear winner. Mark wanted the last word, and here it is: > If you are already pairing Cursor or VS Code with a CLI agent, whether Claude Code, Codex, or Gemini, run OpenCode properly and compare. What I keep seeing is that the IDE is where you look at your files, and the terminal is where the serious work gets done. If that already describes your day, the swap is worth testing. The narrower questions each deserve their own piece: [VS Code against Cursor](/ai-coding-tools/comparisons/cursor-vs-vscode/) as the base editor, [OpenCode against Claude Code](/ai-coding-tools/comparisons/claude-code-vs-opencode/) as terminal agents, and [an IDE against a pure terminal agent](/ai-coding-tools/comparisons/terminal-vs-ide-agents/). In mid-2026, this was never quite a single matchup. ## Your Turn I'll close with the part I keep chewing on. I've tried OpenCode a few times, and Mark has done his best to get me to give it a proper go. I go to meetups where people running local models on OpenCode tell me it is the best thing they have used, and I still do not get it over Cursor, Claude Code, or Codex. So I will put the question to you directly. What am I missing that so many developers love? Have I covered the real points in OpenCode's favour, or left out something that changes the picture? If there is a workflow, a model, or a setup I should try before I write it off, tell me. I think Cursor is still the more obvious choice for most people, and I will update this article if you persuade me. --- ## Cursor vs VS Code: Should You Switch? URL: /ai-coding-tools/comparisons/cursor-vs-vscode/ Cluster: AI Coding Tools > Comparisons Cursor is my default recommendation if you want an AI-focused editor with integrated agent workflows. Stay with VS Code if required Microsoft extensions or your organization's setup make changing editors costly. Both editors can search a repository, edit several files, run checks, and manage agents, so the decision is about workflow, compatibility, and total cost. | What matters | Cursor | VS Code + Copilot | | Form | Standalone editor (VS Code fork) | Extension in your editor | | Repo awareness | Codebase indexing with remote embedding processing | Workspace search and repository context | | Agents | Agents Window, parallel, Automations | Agent mode and Agents window | | Reach | Cursor editor, CLI, and cloud | VS Code, JetBrains, Neovim, Xcode | [Choosing between coding tools](/ai-coding-tools/comparisons/) involves more than picking an editor. Our [AI coding assistant guide](/ai-coding-tools/) compares the tools that can help with the actual coding work. If GitHub Copilot is the assistant you plan to use inside VS Code, read [Cursor vs GitHub Copilot](/ai-coding-tools/comparisons/cursor-vs-copilot/) for the closer product comparison. ## How do Cursor and VS Code differ for AI coding? Cursor integrates its editor and agent tools into one product, while VS Code lets developers add Copilot and other supported agents to their existing editor workflow. Both support multi-file agent work. The [VS Code agent guide](https://code.visualstudio.com/docs/agents/overview), checked October 16, 2026, documents local agents, Copilot CLI, cloud agents, and third-party agents under one set of sessions. Cursor still starts from an AI-first editor design. Its inline edit, Tab completion, repository context, visual diffs, Agents Window, cloud agents, and Automations come from one editor account. Automations can start work from a schedule, Slack message, or GitHub event. VS Code offers a wider set of agent types inside Microsoft's editor and GitHub workflow. Cursor has the more unified surface; VS Code gives a team more ways to keep its existing editor and hand work between local, command-line, and cloud agents. Copilot has closed many headline gaps: agent mode handles multi-file edits, Copilot Edits works inside VS Code, and the GitHub integration is native. Assign Copilot to a GitHub issue and it opens a pull request, which is a clean workflow for teams already living in GitHub. Cursor earns its premium on whole-feature work, where an agent needs to read files, run tests, and recover from failures. For small edits and single-file questions, Copilot and Cursor are closer than their marketing suggests. Parallel agents also add review work, because each task can produce a separate diff and terminal trail. Cursor is useful when you are willing to review that output; it is less useful when you want to stay in a single, conversational coding loop. ## What do Cursor and VS Code have in common? Cursor and VS Code share an open-source editor foundation, including familiar keyboard shortcuts, a file tree, an integrated terminal, debugging tools, and Git controls. Cursor builds its own product on that foundation. Cursor imports your VS Code settings and keybindings in one step during setup. Our [Cursor guide](/ai-coding-tools/cursor/) walks the whole first hour if you want a hand. Your project files, Git history, and configs stay on disk in the same format. Both editors open the same folder, so testing Cursor and returning to VS Code has a low switching cost. ## Extensions: The Marketplace Catch The Marketplace can be the blocking switching cost. Microsoft licenses some extensions for official VS Code builds, including Pylance, the C/C++ tools, and parts of the Remote Development pack. Cursor relies on Open VSX and other available extensions. For JavaScript and TypeScript work, common tools such as Prettier, ESLint, GitLens, and most themes run in Cursor without adjustment. Python developers tend to notice the gap more because the Pylance replacements behave differently. If your day depends on Remote SSH into a specific corporate setup or on the Microsoft C++ debugger, test that workflow inside Cursor's trial before you switch. This compatibility gap is a sound reason to remain on VS Code. ## Pricing and Usage [Cursor pricing](https://cursor.com/pricing) and [Copilot plans](https://github.com/features/copilot/plans) list current pricing. Both products have free and paid paths. The table records current list prices, not a quote for tax, annual billing, or extra usage. | Plan | Cursor | VS Code + Copilot | | Free tier | Free (limited usage) | Copilot Free (2,000 completions/mo) | | Solo, paid | Plus $8; Premium $30/mo | Copilot Pro, $10/mo | | Team | Teams from $30/seat/mo | Copilot Business, $19/seat/mo | Both vendors sell higher tiers and extra usage. Check the live plan and usage dashboard before renewal because model allowances can move separately from the seat price. For teams, the invoice includes more than the seat. Copilot can roll out inside existing editors. Cursor asks the team to test a new editor standard, privacy settings, and Microsoft-only extension gaps. The trial needs to show whether Cursor's agent work reduces more time than its extra review and migration work add. Track the same costs for Copilot's cloud agents and pull-request reviews. Team math compounds quickly across every paid seat. A 20-person team pays $220 more each month for Cursor Teams than Copilot Business before any added model usage, so the evaluation needs a concrete productivity case instead of a vague preference for a new editor. ### Run the Team Math Seat price is the visible part of the decision. Add the time needed to migrate editor settings, replace blocked extensions, update onboarding material, configure privacy policy, and teach developers how agent approval works. Then add any usage-based model spend above the included allowance. Use the trial to estimate review time instead of guessing at "productivity." If Cursor saves fifteen minutes on a feature but adds ten minutes of agent-diff review, the net gain is five minutes. Multiply that measured gain by the number of suitable tasks. Copilot should receive the same treatment, including time spent correcting completions or reviewing cloud-agent pull requests. At the checked list prices, a 20-seat comparison produces an $600 Cursor Teams line and a $380 Copilot Business line before extra usage. The $220 difference belongs beside measured migration, review, and repair time. Recheck both vendor pages before using the arithmetic in a budget. ## Switching Costs and Lock-In Cursor imports VS Code configuration, so much of the editor remains familiar. The remaining costs sit in updates, team conventions, proprietary extensions, and account policy. VS Code now ships roughly weekly, and Cursor rebuilds on top of each release, so core editor features can land in Cursor a little later. Team friction appears when shared launch configs or workspace recommendations depend on Microsoft-only extensions. Cursor adds its own account, billing, and privacy settings on top of whatever GitHub already holds for you. Review privacy mode before pointing an agent at client code, and check your company policy on code leaving the building. Cursor lock-in is shallow because the repository remains editor-neutral. The `.cursor/rules` files are plain text, and Copilot has equivalent instruction files, so those habits transfer when you switch tools. A sensible trial keeps VS Code installed, imports your settings into Cursor, and runs the same repository for a week. That gives you an extension check, an agent-workflow check, and an easy route back if the trade does not hold. ## How should you compare Cursor and VS Code on your own project? Compare Cursor and VS Code by giving both editors the same repository task and acceptance criteria. Measure changed files, failed checks, correction time, extension compatibility, and the cost shown in the usage dashboard. These recommendations are not based on daily Copilot or VS Code use. Your workflow may differ, and benchmark tables age fast in this market. Test the extension and agent workflow that defines your own day before moving a team. ### A Repeatable Seven-Day Trial Keep both editors installed and open the same Git repository in each one. Do not compare a polished Cursor demo with an empty Copilot prompt. Use one branch at a time, reset the task between runs, and give both agents the same acceptance criteria and project instructions. 1. Import the VS Code settings into Cursor and note every extension that fails to carry across. 2. Run one inline-completion task and one selected-code explanation in each editor. 3. Assign a bounded multi-file edit with a named test command and forbidden files. 4. Inspect the changed-file list, diff size, test result, retries, and agent usage. 5. Repeat the task that best represents your paid work before choosing a plan. For a team trial, ask two people with different stacks to repeat it. A TypeScript developer may see almost no extension friction, while a Python, C++, or remote-work user can hit a proprietary Microsoft dependency in the first hour. One person's smooth setup does not prove the editor is safe to standardize. ### Isolate the Trial State Use one test branch at a time and start each editor run from the same commit. Do not let two agents edit the same working tree. Save the task brief and project instructions with the trial result. Import settings once, then record what changed. Cursor-specific rules, Copilot instructions, extension settings, and account policy can make later runs differ even when the repository commit is the same. Keep test data out of personal and production accounts. A fake service, local fixture, or dedicated test account can prove editor and agent behaviour without exposing messages, credentials, or customer records. ### Test the Extensions That Can Block a Switch Make a short inventory before installing Cursor. Mark each extension as convenience, productivity, or required. A theme usually has an easy replacement, while a language server, debugger, remote connector, security scanner, or company-auth extension may control whether you can do the job at all. Open the project, trigger the feature, and inspect the result. An extension appearing in the list proves only that it installed. Confirm that Python analysis resolves imports, the debugger hits a breakpoint, Remote SSH reaches the expected host, and any corporate sign-in completes. Record the alternative extension and its limitations when the Microsoft build is unavailable. Teams should also inspect workspace recommendations and onboarding docs. If the repository installs a Microsoft-only extension by ID, a move to Cursor can break the setup path for every new contributor even when existing developers have a manual workaround. ### Record the Fallback For every required extension, name the Cursor result: same extension works, replacement passes, manual workaround exists, or no working path. Link the licence and install source for any replacement the team approves. Test more than installation by running the language server, hitting a debugger breakpoint, opening the remote target, completing company sign-in, and executing any security scan. Record each failure at the exact step where it occurs. Keep the VS Code path working until the replacement has passed. A team can allow Cursor for repositories that use portable extensions while retaining VS Code for a C++, remote, or company-auth workflow that has no tested substitute. ### Pilot the Team Policy Use a small repository group before an organization-wide rollout. Apply the intended model list, privacy setting, agent permissions, spending cap, and pull-request rules. Confirm that administrators can inspect usage without reading source content they do not need. Ask a second developer to set up from the written guide on a clean machine. Record missing extensions, manual account steps, policy drift, and time to the first passing build. Existing users often hide setup work in old local state. Define the rollback before expanding the pilot. The repository should still open in VS Code, the required extension list should remain valid, and Cursor-only rule files should not block contributors who use another editor. ## Evidence Before Switching Write down the evidence before changing the team standard: - The required language servers, debuggers, remote tools, and sign-in extensions passed. - The same bounded task produced a smaller or easier-to-review diff. - The project checks passed without manual repair omitted from the trial record. - The account, privacy, model, and usage settings match company policy. - The monthly invoice plus review time fits the budget model. - The fallback to VS Code was tested with the same repository. Include one failed task because a trial made only of successful demos cannot show how each agent reports uncertainty, recovers from a bad edit, or exposes the cost of retries. ## Verdict on Cursor vs VS Code Cursor remains my default editor and our number-two AI coding tool overall, behind Claude Code. Its editor, parallel agent workspace, and Grok Bot on Premium and higher plans suit how our team works. That is a preference, not proof that it wins every feature comparison. VS Code is a sensible choice when Microsoft-only extensions or an established organizational setup matter more than migrating to Cursor. The last team member who used VS Code with Copilot daily has moved off both, but your compatibility test may lead to a different decision. ## FAQ ### Is Cursor just a VS Code fork? Cursor is a VS Code fork with additional AI features, and that shared foundation is useful. Shortcuts, the terminal, themes, and most extensions carry over. The difference is everything built on top: full-repo indexing, Cmd+K edits, and an Agents Window that has been Cursor's default way of working since 3.0, alongside editor-based agent work in VS Code. ### Can I use my VS Code extensions in Cursor? Most VS Code extensions work in Cursor as installed. Cursor imports your setup and pulls extensions from the Open VSX registry. The exceptions are Microsoft's proprietary ones, including Pylance, the C/C++ tools, and parts of Remote Development. Test those workflows during the trial if you rely on them daily. ### Is a paid Cursor plan worth it? A paid Cursor plan is worth it when it saves enough editing and review time to justify the subscription and any extra usage. Plus is $8 per month; Premium is $30 per month for broader models and cloud features. Test one week of ordinary tasks and record corrections, checks, review time, and usage before choosing. ### Can I switch back to VS Code from Cursor? Developers can switch back from Cursor to VS Code because your project files, Git history, and standard configuration remain in the repository. Keep VS Code installed during the trial, export any Cursor-only rules you need, and test the Microsoft extensions that may not have an Open VSX replacement. ### Does VS Code with Copilot have agent mode now? VS Code with GitHub Copilot supports agent workflows that find relevant files, make changes, and run checks. Its Chat view and Agents window let you supervise work in different layouts, and supported harnesses can connect local, command-line, or cloud sessions. Check each feature's requirements rather than relying on an old blanket preview label. --- ## Open Source vs Proprietary AI Coding Tools (2026) URL: /ai-coding-tools/comparisons/open-source-vs-proprietary/ Cluster: AI Coding Tools > Comparisons Choose an open-source coding client when you need to inspect its code or control the model provider. Choose a managed proprietary tool when its integrated workflow saves more work than maintaining your own stack. For privacy, inspect the deployment: an open client can call a hosted proprietary model, while an open-weight model can run either locally or on someone else's servers. That matters when you are buying a tool for privacy, cost control, or freedom to switch providers. The license on the client does not decide where your code goes. The model, endpoint, account settings, and network around it do. ## Which Setup Fits Your Coding Workflow? Choose a managed tool such as Cursor when you want an integrated coding workflow, OpenCode with a hosted provider when you want an open client without running inference, or OpenCode with a local model when controlling the inference environment justifies the extra work. | Example Setup | Best Fit | Main Tradeoff | | [Cursor](/ai-coding-tools/cursor/) with its hosted model access | Integrated editing and agent work | Vendor-controlled client and service terms | | [OpenCode](/ai-coding-tools/opencode/) with a supported hosted API | Client customization and provider choice | Code still reaches the chosen service; usage and retention depend on that provider | | OpenCode with Ollama and a local coding model | Control over local inference | Hardware, model quality, configuration, and maintenance become your responsibility | [OpenCode documents hosted and local providers](https://opencode.ai/docs/providers/), and [Ollama documents its OpenCode integration](https://docs.ollama.com/integrations/opencode). These examples show why the choice is not simply open versus closed. An open client can use a proprietary hosted model. Selecting Ollama also requires checking that the chosen model and endpoint run locally. checked October 16, 2026: the linked OSI, OpenCode, and Ollama pages are the sources for the definition and product paths below. Model menus can move quickly, but the licence, endpoint, operator, and review questions remain. For the applications that put these models to work, see our [AI coding tools overview](/ai-coding-tools/). Model licensing and client choice are separate decisions. For a concrete local deployment, read [running local models with OpenCode](/ai-coding-tools/opencode/local-models/); for shared infrastructure, use the [enterprise deployment guide](/ai-coding-tools/opencode/enterprise/). ## What Does Open Source Mean for AI Coding Tools? The [Open Source AI Definition 1.0](https://opensource.org/ai/open-source-ai-definition) says an AI system must grant the freedom to use, study, modify, and share it. Those freedoms need the preferred form for making modifications. For machine learning, OSI lists three parts of that preferred form: data information, the code used to train and run the system, and model parameters. Downloadable weights provide only the parameters, and the model licence may also limit uses that an OSI-approved licence would permit. | What You Get | Open Weights | OSI Definition | | Model weights | Yes | Yes | | Training code | No | Yes | | Training-data information | No | Detailed provenance and access information | | Material for modification | No | Yes | | Any-use license | Often restricted | Yes | ## How Do Open-Source Clients and Open-Weight Models Differ? An open-source client exposes the application code that manages your coding workflow; an open-weight model makes its trained parameters available to download. The client license, model license, and hosting arrangement are separate choices. ### Layer 1: Open Source Clients The client can have an open licence even when the model and endpoint do not. This is common with terminal agents and editor extensions that let you add provider credentials. [OpenCode](/ai-coding-tools/opencode/) publishes its client source and lets you configure hosted or local providers. Its [provider docs](https://opencode.ai/docs/providers/) show API credential, OAuth, and system-variable paths. The chosen provider still controls the endpoint. A client licence lets a team inspect and modify client code under that licence. It does not grant rights to a hosted model, copy the provider's service into your network, or set the provider's retention terms. ### Layer 2: The Models (Open Weights, Not Open Source) Open-weight releases let you download model parameters and run inference yourself. That can give you a local path, but it does not automatically give you the training data, the training code, or an unrestricted commercial license. Read the model card and license before calling it open source. Hosted open models are a separate option. You may select an open-weight model through a cloud API and still send code to a provider. That can be useful for model choice, but it does not meet a rule that source code must stay inside your network. That distinction is easy to miss in a tool picker. A model can be downloadable, an API can be easy to switch, and the request can still travel through a hosted service. Ask four plain questions before treating a stack as private: where does the prompt go, where does the output go, what is retained, and which account can retrieve it? OSI-aligned AI systems publish the material needed to study and modify the system, including code, parameters, and data information. OSI requires enough information to build a substantially equivalent system; it does not require publishing every raw training dataset. That is a higher bar than a downloadable checkpoint. ## Open-Source vs Proprietary Tradeoffs Data path: A hosted model sends selected content to a service endpoint. A self-hosted model can keep inference inside a controlled network, but only when telemetry, updates, and fallback routes follow the same rule. Modification rights: Open weights may permit local inference or fine-tuning under their licence. OSI's definition asks for more: the code and data information needed to study and modify the system. Read the exact release terms before training on company code. Portability: An open client can make provider configuration portable. Switching still requires compatible prompts, tools, model identifiers, credentials, and tests. A provider switch is complete only after the replacement passes the same task checks. Operations: Hosted services run inference and publish service terms. Local stacks move patching, capacity, backups, monitoring, and incident response to your team. Neither route removes code review or security testing. ## How Should You Compare Coding Quality and Support? Compare coding quality by testing the complete client-and-model combination on your own repository, and compare support by checking who is responsible when that combination fails. An open-source license or proprietary label does not establish how reliably an agent will complete your task. Use the same bug fix or small feature and judge correctness, unrelated edits, passing tests, and review effort. A local model that fits in memory may still struggle with the context or tool use your project needs. A hosted model may avoid the hardware work while introducing usage limits and service dependencies. For support, distinguish community documentation and issue trackers from a paid support agreement. Check the actual response commitments and supported configurations rather than assuming every proprietary plan includes dedicated support or every open-source setup leaves you on your own. ## Provider Access Can Shift An open client does not turn a consumer subscription into a durable API contract. Provider authentication, account eligibility, model names, and billing routes can move independently of the client release. A connection that works today may fail after the provider changes its rules. Use the authentication route named in the provider's current documentation. Do not build a production workflow around copied session tokens, client impersonation, or an undocumented subscription path. Record who owns the provider account and what migration path exists if access changes. Local inference removes a hosted inference endpoint from that path. It does not remove model licence terms, runtime updates, hardware failure, or the work needed to replace a model. Portability must be rehearsed, not inferred from the client licence. ## What Hardware Do You Need for Local Coding Models? Local coding models need enough memory for the model and its working context, plus processing capacity to respond at a useful speed. The required hardware depends on the model, quantization, context setting, and runtime. A model must fit while the editor, browser, and build tools are also running. It must then produce output quickly enough for the intended task. | Measure | Why It Matters | Trial Evidence | | Peak memory | Fit beside dev tools | System monitor | | Output rate | Waiting time | Timed task | | Task pass rate | Usable results | Checks and diff | | Review time | Total work | Reviewer log | ### What Does Running a Local Coding Model Cost? Running a local coding model costs hardware, electricity, storage, and the time needed to operate and maintain it. You also own download time, disk space, model updates, prompt routing, backups, and the time spent finding a model that fits the task. A smaller model may be fast and private, then fail on the same repository task that a hosted model completes in one pass. Run a small trial before buying hardware. Use one representative task, measure the setup time and review time, and compare the result with the cloud tool you would otherwise use. A local system is a good result only when it meets your privacy rule and still helps you ship work. Compare the local trial with the hosted route on the same task. Include electricity, staff time, failed runs, storage, and replacement hardware. Do not invent a break-even month from list prices alone. There is also a maintenance cost after the first successful run. Someone must patch the runtime, choose new model releases, watch disk use, and decide whether a failed local result is a model limitation or an application bug. That work may be justified, but it should be part of the decision rather than a surprise after the hardware arrives. ### Test Capacity and Recovery Test a local model with your editor, browser, build tools, and realistic repository context running. Check memory use, response speed, and recovery after a restart. If offline operation matters, confirm that an unavailable local model does not trigger a hosted fallback. Our [local-model setup guide](/ai-coding-tools/opencode/local-models/) covers the practical configuration. ## Operating and Maintaining Your Stack ### Who Maintains an Open-Source AI Coding Stack? Your team operates and updates the open-source components it runs itself, while a hosted provider maintains the model service you use. Open source moves responsibility; it does not remove it. With a hosted proprietary tool, the vendor runs the model service, patches the client, and sets the model menu. With an open client and a hosted provider, you choose the provider but still depend on its availability and terms. With local inference, your team owns the runtime as well. That ownership is useful when it solves a named problem. A company that must keep source code inside a controlled network may accept the cost of a local model server. A team that needs two providers for resilience may prefer a client that can switch endpoints. Neither case means every developer should become the on-call person for GPU drivers and model updates. Write down who owns credentials and model approval. Name the operator who responds when an update alters tool behavior. If nobody owns those jobs, the apparent freedom can turn into an unmanaged production dependency. ### Check Licenses and the Data Path Check the client license, model license, provider terms, and deployment settings separately. Record the versions you tested, the endpoint that receives code, and who operates the service. An open client does not grant rights to its model or guarantee that prompts stay inside your network. For a local-only requirement, check telemetry, updates, and fallback connections as well as the inference endpoint. Keep the result with your configuration so a later provider or model change can be checked against the same requirement. The [OpenCode enterprise guide](/ai-coding-tools/opencode/enterprise/) covers the detailed deployment checks. ## What Should You Check Before Choosing a Tool? Before choosing an AI coding tool, check its client and model licenses, data destinations, operating costs, and provider-switching options against a testable requirement. "We prefer open source" is too vague, while "prompts must stay on this network," "the client must be forkable," and "we need two replaceable model providers" can each be verified. - Which client licence applies to the version being deployed? - Which model licence applies to commercial and modified use? - Which endpoint receives prompts, files, tool output, and logs? - Who owns credentials, updates, spend limits, and incident response? - Which task and check prove that a provider or model replacement works? Run the test with a non-sensitive repository first. Capture network destinations, permissions, output logs, model identity, and the final diff. Repeat after a provider switch or model update so you know the replacement works as required, rather than relying on a sales promise. ### Test a Provider Switch Save your configuration and test one representative coding task with a second provider before relying on portability. Compare edits, passing checks, permissions, and review time. A replacement that answers chat questions but cannot complete the agent's task is not a working fallback. ## Verdict Use a proprietary stack when vendor-run inference and account policy meet the requirement. Use an open client when inspecting the client or changing providers is part of the requirement. Self-host when the network boundary justifies owning inference operations. The accepted trade-off is vendor control against operator workload; the words "open source" do not settle the licence or data path by themselves. ## FAQ ### Are open-weight models open source? Open weights are not necessarily open source. The term usually means you can download and run the model. The Open Source AI Definition also requires the code and data information needed to study and modify how the parameters were produced. ### When does local inference make sense? Local inference can meet an offline or network-boundary rule when the chosen model also passes the team's task test. Measure memory use, output speed, review time, and result quality on the hardware that will run it. ### Can an open-source tool use a proprietary model? An open client such as OpenCode can call a proprietary API, while a proprietary editor can expose an open-weight model. Client licensing and model licensing are separate records. ### Can an open-weight model meet a strict privacy rule? An open-weight model meets a strict privacy rule only if its entire deployment path complies with that rule. Confirm the runtime's network policy, telemetry, update route, fallback endpoints, logs, and credential storage before treating a downloaded model as local-only. ## Related Comparisons - [Best open-source models for coding (2026)](/ai-coding-tools/comparisons/best-open-source-models/): specific model recommendations, benchmark context, and how to access them via OpenRouter - [Claude Code vs OpenCode](/ai-coding-tools/comparisons/claude-code-vs-opencode/): the same terminal-agent idea, one commercial and one open source, compared directly - [AI coding tool comparisons](/ai-coding-tools/comparisons/): full hub with all head-to-head matchups --- ## Terminal vs IDE AI Agents: When to Use Each URL: /ai-coding-tools/comparisons/terminal-vs-ide-agents/ Cluster: AI Coding Tools > Comparisons Use a terminal agent when command-line work and a task brief fit your workflow; use an IDE agent when you want to inspect and steer changes beside the code. Both can search the repository, edit several files, and run checks. I run Claude Code and Codex side by side on separate branches, then use Cursor cloud agents against a deployed dev site. The useful distinction is how I supervise each task, not which interface has access to the files. The products covered here are [Claude Code](/ai-coding-tools/claude-code/), [OpenAI Codex](/ai-coding-tools/codex/), [OpenCode](/ai-coding-tools/opencode/), [Cursor](/ai-coding-tools/cursor/), and [GitHub Copilot](/ai-coding-tools/copilot/). The labels blur as vendors add terminal commands, editor extensions, background work, and browser tools. The useful distinction is still plain: are you staying in the edit loop, or describing a bounded outcome and reviewing the work afterward? Once you know which workflow suits the task, our [recommended AI coding tools](/ai-coding-tools/) provide a shortlist of products to try. ## What Is the Difference Between Terminal and IDE Agents? Terminal agents run through a command-line interface, while IDE agents place the interaction and review inside a code editor. Both can search repositories, edit files, and run commands; the difference is how you interact with and supervise that work. Terminal agents begin in your shell, where you describe a task and the agent reads the codebase, writes code, runs approved commands, and reports back. It can run a build, inspect a failure, check Git history, and feed command output into the next step. IDE agents work inside an editor and can search unopened files, coordinate edits, and run commands through tools. The same product may also offer inline completions and selected-code chat, but those are different interaction modes. A visual diff keeps the review beside your code. The table compares the terminal and editor surfaces, rather than treating each product as belonging to only one category: | | Terminal Agents | IDE Agents | | Examples | Claude Code CLI, Codex CLI, OpenCode CLI | Cursor, GitHub Copilot, Google Antigravity | | Where it lives | Any terminal | Inside specific editors | | Interaction style | Describe the task, review results | Describe tasks and review changes beside the code | | Task pattern | Multi-file and command-led work | Interactive edits and multi-file agent tasks | | Context | Can search the repository | Editor context plus repository search | | Authority | Can run approved commands | Can run approved commands and show diffs | | Interrupts your flow | Depends on your terminal workflow | Can stay beside the current edit | | Requires a specific IDE | No | Yes | ### Authority Does Not Follow the Interface An IDE agent can open a terminal, edit many files, call services, or work in the cloud. A terminal agent can run in a restricted sandbox and ask before every write. The window does not tell you how much authority the agent has. Check the permission mode, workspace trust, allowed paths, network access, service identity, and remote repository grant for the exact surface in use. Do this again when moving a task from editor chat to a cloud agent. Keep high-impact actions outside both defaults by requiring a separate approval point for deploys, billing edits, production data writes, account grants, and irreversible migrations, even when the coding task is otherwise safe to hand off. ## When Should You Use a Terminal Agent? Use a terminal agent when your work centers on shell commands, remote environments, or repeatable tasks you can describe with a clear completion check. These are workflow advantages, not capabilities exclusive to terminal interfaces. ### Cross-File Work Across Dependencies A type rename, package migration, or authentication update can touch routes, config, and tests. These jobs require repository search before the first edit. A terminal agent can inspect the dependency path, but it still needs a narrow scope and named checks. That is the point at which I split the work into a separate branch and give the agent a written boundary: which paths it may touch, which checks it must run, and which changes need approval. The useful result is not a claim that the agent understood everything. It is a set of changes small enough for a person to check and spot mistakes. For example, a dependency migration is a good terminal task when the outcome is explicit: replace one package, update only the affected imports, and run the build and focused tests. "Clean up the project" is not a good terminal task. It hides too many decisions and makes a large diff look more competent than it is. ### Tasks You Want to Walk Away From Terminal agents are built for task hand-off. You describe the task, approve a plan or commands, and review the result later. [OpenAI Codex](/ai-coding-tools/codex/) also offers background execution, so a bounded task can produce a branch or pull request while other work continues. ### Working Outside Your Primary Editor A terminal agent is convenient when you already work over SSH or need command output from a build or deployment tool. IDE agents can also run commands, search unopened files, and connect to remote workspaces. Prefer the surface that exposes the relevant environment and checks clearly. ## When Should You Use an IDE Agent? Use an IDE agent when you want to inspect code, steer changes, and review diffs inside your editor. An IDE workflow is especially useful when you alternate between manual editing and agent assistance. ### Inline Editing and Quick Changes An editor workflow keeps a small function edit or selected-code change beside the original code. Tools such as `Cmd+K` in [Cursor](/ai-coding-tools/cursor/) support that loop without starting a separate terminal task. Inline editing is a related editor feature, rather than a capability that defines every IDE agent. ### Visual and UI Work Cursor and Copilot can work from the file and selection in view. That helps with JSX, CSS, and UI trees where the current edit matters. A terminal agent can generate the code, but the interface does not prove that the rendered layout works. That does not make an IDE agent safe by default. A CSS edit can still damage a narrow viewport or a UI state you did not open. The advantage is that the place you need to inspect is already on screen. Use the editor loop to make the small edit, then open the affected page and check it at the viewport the reader will use. ### Reading Unfamiliar Code Cursor's Chat and Copilot's explain features let you ask questions about code you're currently reading. Select a function, ask what it does, and get an explanation in the editor. This interaction is better suited to reading than execution. ### Autocomplete and Flow State AI editors can offer autocomplete alongside their agent features, so a small edit stays in the same window. Autocomplete and agent mode are separate interactions. A terminal task asks you to stop editing, describe an outcome, and return to the diff. That extra hand-off is useful for larger jobs and wasteful for a one-line fix. ## How Do Automation and Costs Differ? Terminal workflows can fit naturally into scripts and repeatable command-line jobs, while editor workflows keep interactive review close to the code. Cost depends on the selected product, model, plan, and usage rather than whether the interface is a terminal or an IDE. If automation is your goal, check whether the particular agent supports non-interactive execution, usable output, and the permissions your job requires. A terminal interface alone does not guarantee those features. Editor products can also offer scheduled or cloud agents, so compare the actual execution mode rather than assuming background work requires a CLI. Compare the subscription, included allowance, separately billed model usage, and time spent reviewing results. Repeated attempts and long-running tasks can consume usage in either interface. Run one representative task through each workflow before deciding that one costs less. ## Tool Breakdown Claude Code is a coding agent available in terminal, desktop, IDE, and web surfaces for delegated, constrained work. It suits large refactors, migrations, and tasks that need repository-wide reasoning before an edit begins. Check Anthropic's current model and plan pages before making a purchase decision from a model name. OpenAI Codex runs background tasks you can review as a pull request or branch. The work needs a clear scope, project checks, and a reviewer who can inspect the result later. OpenCode is the open-source terminal agent option, model-agnostic and MIT-licensed. You bring your own API keys or run local models through Ollama. Useful when you want an agent you can inspect, modify, and run without a subscription dictating your model choice. Cursor keeps AI work inside an editor workflow and is our number-two tool overall, behind Claude Code. Its inline and agent features suit developers who want to remain close to the files and accept changes in context, and Grok Bot on Premium and higher plans adds background work with weekly usage limits and separate local-access controls, which blurs this page's terminal-versus-IDE line in Cursor's favor. GitHub Copilot provides inline completion, chat, agents, and pull-request review inside GitHub and several editors. Team plans add central billing and admin policy, which is its remaining case; it has dropped down our rankings and none of us run it daily. Check the current plan page before relying on a model or allowance. ## Should You Use Both a Terminal Agent and an IDE Agent? Use both a terminal agent and an IDE agent when each provides a useful workflow that the other does not handle as comfortably. A two-tool setup can separate bounded repository work from close editor work. The pairing is useful only when each tool has a named job and the hand-off does not hide who changed a file. One pattern is task + refinement: give the bounded multi-file task to the terminal agent, review its diff, then use the IDE for selected edits and visual checks. Keep one branch owner during the hand-off. A file-count threshold is too crude on its own. A one-file payment edit can need closer review than a five-file docs update. Route by consequence, proof, and whether judgment must stay continuous. An IDE pilot on a small, reversible edit keeps the suggested code beside the original and shows how much checking the output needs. A terminal pilot becomes testable once the task has a clear outcome and a clean review path. ### A Quick Routing Test The task defines the supervision needed, and a short brief that names the result, paths in scope, and proof command makes a terminal hand-off testable. If the right answer depends on a UI element or screenshot, use the workflow that lets you inspect the rendered result and steer changes easily. For example, "replace this form label and confirm the mobile layout" needs a mobile browser check regardless of which agent edits the code. "Trace why this validation rejects a legitimate input, add a regression test, and run the focused suite" is a better delegated task because the expected behaviour and evidence are concrete. - Start terminal work on a branch or isolated task scope. - Ask the agent to list files, commands, and unverified assumptions. - Use a browser to inspect the rendered result at the affected viewport. - Stop and split the task when the agent needs a product decision. Vendor surfaces keep converging: GitHub documents Copilot in editor, CLI, and cloud-agent workflows, while Claude Code and Codex have added more background options. The routing test still holds because it is about supervision: stay close when judgment is continuous, and delegate when the work has a stable acceptance test. ### Write the Handoff When a terminal task moves back to the IDE, record the branch, last commit, changed paths, checks run, and anything the agent could not prove. Open the diff before making a follow-up edit so the second tool does not hide the first tool's mistake. When an IDE investigation moves to the terminal, pass the selected file, observed behaviour, reproduction steps, and expected result. Do not replace that evidence with a fresh broad prompt. The point of the hand-off is to preserve what was learned. Keep one owner for the branch during the transfer. Parallel agents editing the same paths can produce a clean-looking diff whose history no longer explains which task introduced a line. ## Verdict Use an IDE agent when continuous file or visual judgment matters. Use a terminal agent when the outcome, boundary, and proof can be written before the work begins. The accepted trade-off is tighter in-editor inspection against a broader shell-and-repository hand-off. Add a second surface only for a job the first one cannot handle well. See the [AI coding tool comparisons hub](/ai-coding-tools/comparisons/) for specific pairings. ## FAQ ### Terminal agent or IDE, how do I decide? Choose a terminal agent for a bounded task with a clear acceptance check; choose an IDE workflow when the task needs continuous visual judgment and editing. ### Can I use both? Developers can use both a terminal coding agent and an IDE by keeping one branch owner and one clear hand-off at a time. Review the terminal agent's diff before opening the same files for IDE edits, or you can lose track of which tool introduced an edit. ### Which is better for beginners? An IDE-based AI coding workflow is usually easier for beginners who want to compare original and suggested code side by side. A beginner can still use a terminal agent safely when the task is small, reversible, and paired with a named check. ### Can the same AI coding tool work in both a terminal and an IDE? The same AI coding tool can offer terminal and IDE interfaces, although the available features depend on the product and interface. Choose the interface that supports your task and preferred review process rather than assuming a tool works only in its original format. ### What should I keep out of an agent task? Unsupervised coding-agent tasks should exclude personal accounts, production credentials, irreversible migrations, and vague cleanup work. An agent can investigate or prepare a proposal, but the task should stop before the hard-to-reverse action. --- ## GitHub Copilot Setup and Enterprise Rollout URL: /ai-coding-tools/copilot/setup-and-enterprise/ Cluster: AI Coding Tools > GitHub Copilot Set up GitHub Copilot by confirming account access, enabling one editor or agent surface, adding repository instructions, and testing one small task. For a team rollout, also check model policies, content-exclusion coverage, and the AI-credit budget before assigning more seats. Copilot is not in my daily rotation, so my evaluation depends on measured pilot results and controls I can verify. The [GitHub Copilot guide](/ai-coding-tools/copilot/) covers the wider product. If your team is still choosing a product, review the [AI coding tools comparison](/ai-coding-tools/) before applying a Copilot-specific rollout plan. If the rollout also involves a choice between editors, [Cursor vs GitHub Copilot](/ai-coding-tools/comparisons/cursor-vs-copilot/) compares the day-to-day workflows. ## Choose the First Copilot Surface Pick the surface where the first trial task already happens. A developer who writes in VS Code should begin with the editor extension. Someone working over SSH may learn more from Copilot CLI. A team that wants issue-to-pull-request work should pilot the cloud agent in a low-risk repository. | Work Starts In | Copilot Surface | First Trial | | Editor | IDE extension | Explain one file | | Terminal | Copilot CLI | Read-only project map | | GitHub issue | Cloud agent | Small tested fix | | Pull request | Code review | Review known defect | Keep the first pilot to one surface. Each one has different context, policy, and review behavior. One clear task gives you evidence about the product; four simultaneous trials give you results that are hard to compare. ## Install and Confirm Access For an editor trial, install the official Copilot extension or plugin in the developer's existing client. Sign in with the GitHub account that received the Copilot seat. Confirm the account and organization before diagnosing a missing feature, since personal and work identities can have different plan access. Begin in a non-sensitive repository with a task the developer understands. Ask Copilot to explain a known module, name the test command from the repository, or draft a test plan without editing. Check its file references. A plausible answer that points at the wrong code is a failed setup test. Then allow one reversible edit. Give the expected file list and validation command in the prompt. Review Copilot's plan, full diff, terminal commands, and test output. Record which model and surface ran the job, because the same prompt can behave differently across clients. ## Write Repository Instructions Repository-wide instructions live at `.github/copilot-instructions.md`. GitHub says these instructions are added to Copilot requests made in that repository. The file should state facts Copilot otherwise has to rediscover: the build command, test command, project layout, coding rules, and known unsafe actions. Write rules that a reviewer can check. "Use pnpm and never npm" is testable. "Write clean code" is not. Name the exact command and the area it applies to, but do not turn the file into a copy of every design discussion the team has had. Instruction File Example Use the package manager named in `packageManager`. Run the focused test before the full build. Do not add dependencies, edit generated files, or change database migrations unless the task asks for that path. Report every changed file and command. Commit the file with the repository so changes receive normal review. Add a rule after a repeated, expensive mistake or a settled team decision. Remove a rule when the codebase no longer follows it. Stale instructions can be worse than no instructions because they make wrong output look compliant. ## Add Path-Specific Agent Rules One file may not fit a repository with different languages or risk zones. Path-specific instructions use files named `NAME.instructions.md` under `.github/instructions/`. Their frontmatter uses `applyTo` with one or more glob patterns. A rule for `app/models/**/*.rb` can name the model test command without cluttering frontend work. A deployment rule can require review for files under `infra/`. GitHub currently limits path-specific instructions on GitHub.com to the cloud agent and code review, so verify client coverage before depending on them elsewhere. Copilot also reads `AGENTS.md` for coding agents. You can place more than one in the tree, and the nearest file takes precedence. A root `AGENTS.md` can hold shared rules while a file near the payment code sets stricter checks for that area. - Repository-wide: `.github/copilot-instructions.md` - Path-specific: `.github/instructions/NAME.instructions.md` - Agent-specific by location: one or more `AGENTS.md` files - Personal: keep personal preferences out of team policy files ## Configure Content Exclusions Content exclusions let repository administrators, organization owners, and enterprise owners tell Copilot to ignore named files or paths. GitHub documents this for Business and Enterprise. The setting excludes files from supported suggestions, Chat, and code-review features; it is not an access-control system. The boundary has gaps. GitHub says content exclusions do not support GitHub Copilot CLI, Copilot cloud agent, or Edit and Agent modes in Copilot Chat in IDEs. They do not apply to symbolic links or repositories on remote file systems. An editor may also pass semantic details, such as a type definition, from an excluded file. Use exclusions to control context. Remove credentials from the repository, apply normal access control, and use a secret manager. Test an exclusion from each surface you plan to enable, then document the remaining gaps in the rollout decision. ## Set Up Copilot CLI Copilot CLI is GitHub's current terminal agent and replaces the older `gh copilot suggest` extension. On macOS or Linux, GitHub documents `brew install --cask copilot-cli`. On Windows, it documents `winget install GitHub.Copilot`. Start it by changing into a project and running `copilot`. 1. Install the CLI with the operating system method your team allows. 2. Open the repository directory in a terminal. 3. Run `copilot`, then use `/login` on the first session. 4. Confirm the trust prompt for that directory only after checking the path. 5. Ask for a project summary before allowing an edit or command. Copilot CLI can read, modify, and execute files below the trusted directory. It can ask for access to another directory when the task needs it. Do not start it from a broad folder that contains several unrelated repositories, credentials, or personal documents. Enterprise controls differ from editor policy. GitHub says administrators can control CLI access, cloud delegation, custom agents, and MCP policy. It also says path-based content exclusions and IDE-specific policies do not apply to Copilot CLI. Treat the CLI as its own rollout surface. ## Plan an Enterprise Pilot A pilot should answer a purchase question. Pick a small set of repositories that reflect the languages, test quality, and risk the team handles. Include one ordinary service, one weakly documented project, and one repository where access should remain narrow. Write the review gate before assigning seats. Name who checks agent pull requests, which tests must pass, what requires security review, and which actions are prohibited. A Copilot-generated pull request enters the same branch protection and human review path as any other contribution. - Track tasks attempted and tasks completed with an accepted diff. - Record failed checks, reverted changes, and review findings that held up. - Measure AI-credit use by surface and model. - Log policy exceptions and content-exclusion gaps. - Ask developers which work became easier and which work became harder to review. Avoid a generic acceptance rate. A large number can hide trivial completions or a low review bar. Keep task type, repository, model, time spent, and final review result together so the pilot can explain where Copilot earned a seat. ## Set Model Policy and Budget Organization owners manage features under Copilot policies and models under the model settings. An enterprise owner can enforce a choice above the organization, so an organization may be unable to enable a model that the enterprise disabled. Check both levels when the model picker differs across teams. GitHub listed Copilot Business at $19 per granted user each month with 1,900 AI credits, and Copilot Enterprise at $39 with 3,900 credits, under the current standard allowance. Enterprise requires GitHub Enterprise Cloud. | Plan | Price | Listed Credits | | Business | $19/user/month | 1,900 per user | | Enterprise | $39/user/month | 3,900 per user | Use the current billing page before approval because prices, credit grants, and promotions can move. Decide who owns the budget, whether extra usage is allowed, and what alert triggers a review. New model access should pass the same policy and cost check rather than appearing by default because it is new. ## What to Approve After the Pilot GitHub Copilot is easiest to govern when the rollout follows the work: one surface, one repository, inspectable instructions, a tested exclusion policy, and a written review gate. That is enough to learn whether the tool helps before an organization pays for broad access. Business fits a GitHub team that needs central seat and policy control. Enterprise adds the Enterprise Cloud layer, more included credits, and earlier access to some features. Choose after the pilot shows a need for those controls, then verify the live plan and billing pages before assigning the seats. ## FAQ ### Which Copilot instruction file should I create first? Teams configuring GitHub Copilot should start with `.github/copilot-instructions.md` for repository-wide build, test, and coding rules. Add `NAME.instructions.md` files under `.github/instructions/` only when a path needs different rules. Use `AGENTS.md` for coding-agent guidance that should follow the nearest file in the directory tree. ### Do content exclusions cover Agent mode? GitHub Copilot content exclusions do not support GitHub Copilot CLI, Copilot cloud agent, or Edit and Agent modes in Copilot Chat in IDEs. Treat exclusion as one control, then verify every enabled surface separately. ### Can enterprise admins control Copilot CLI? Enterprise and organization administrators can enable or disable Copilot CLI, control cloud-agent delegation, apply MCP policies, expose approved custom agents, and record policy changes in audit logs. GitHub also states that IDE-specific policies and path-based content exclusions do not apply to Copilot CLI. ### How much do Copilot Business and Enterprise cost? Copilot Business costs $19 per granted seat per month and Enterprise costs $39. Enterprise requires GitHub Enterprise Cloud. Each seat contributes 1,900 or 3,900 AI credits, respectively, to the shared pool; additional usage can add charges. --- ## Cursor Automations: Triggers, Templates, and No-Repo Agents URL: /ai-coding-tools/cursor/automations/ Cluster: AI Coding Tools > Cursor Cursor Automations run work while the editor is closed. They are cloud agents that fire on a schedule or in response to an event, follow the instructions you write, and leave a result where you can inspect it. Repeated review, triage, and reporting become useful automation targets once you decide the permissions, spending, and failure handling before the first schedule runs. My own Cursor use centres on cloud agents against deployed development environments, so I judge an automation by the branch, run log, and review path it leaves behind. Check your plan before setup: current cloud-agent access starts at Premium, while legacy plans have different entitlements. Begin with a read-only report: use the first run to send a summary or comment on a test pull request. Check its scope, permissions, output, and cost before authorising code changes or a recurring production trigger. Automations are a deeper part of Cursor’s workflow. For the broader product choice, our [best AI coding tools guide](/ai-coding-tools/) compares Cursor with the alternatives. For delegation to a persistent cloud computer rather than an event-triggered job, see the [Grok Bot guide](/ai-coding-tools/cursor/grok-bot/). ## The Automations Dashboard The dashboard is a control room rather than an editor view. The top row tracks total automations, successful and failed runs over the last seven days, and a run-history sparkline. Below that sit your own automations and your team's, and under those a marketplace of templates you can add with one click. ![image]()The Automations dashboard, with run-history stats up top and a marketplace of templates grouped by job below. ## How Do You Create a Cursor Automation? Developers can create a Cursor Automation from the Agents Window or the Automations dashboard, choose a trigger, write the task instructions, and configure repositories and tools. Name it, test a narrow run, and activate the recurring trigger once you have checked the output and cost. Alongside a single repository or several, the form offers No Repository. That choice changes the whole character of the automation. With a repo attached, the agent clones your code into a sandbox and works on an agent branch. Repo-backed runs can push that branch and open a pull request by default. A human still reviews the diff and checks before merge. A no-repo run does not clone a repository or use the built-in repository editing and pull-request workflow. It can still read or change resources through connected tools, including MCP servers, according to their permissions. Use it for a digest or research task, then inspect each connection before treating the run as read-only. ![image]()Creating an automation: the repository dropdown can be set to No Repository, which removes the built-in repository workflow; connected tools still determine its external access. ### Set the Operating Boundary Below the scope you write the agent instructions in plain language, with the same `@` for tools and `/` for commands used in the editor. You choose the model from a curated list and attach tools, including Memories by default and any MCP server you connect. Once saved, the automation waits for its trigger. Tell the automation what it may and may not do. Name the event it should handle, the information it may use, where to send the result, and the actions it must avoid. "Summarise new production errors in this channel; do not edit a repository or page anyone" is safer and easier to audit than "handle incidents." ## Which Events Can Trigger an Automation? A schedule, repository event, supported app event, or authenticated webhook can start a Cursor Automation. Choose the narrowest trigger that covers the repeated job, then test its filters before enabling frequent runs. ![image]()The trigger picker, where an automation fires on a schedule, on events from GitHub, Linear, Sentry, Slack, or PagerDuty, or on an inbound webhook. - Scheduled runs on a preset of hourly, daily, or weekly, or on a cron expression you write. Runs can be delayed under load, but they never start before their time. - GitHub, GitLab, and Bitbucket Cloud support core pull-request and push events. Additional triggers vary by provider: do not assume labels or completed CI runs are available for every host. Fork pull requests are not supported except on merge. - Slack fires on a new message in a public channel, with optional keyword filters, or on a channel being created. Private channels are not visible to the trigger. - Linear , Sentry , and PagerDuty cover the issue, error, and incident side: a new Linear issue or status change, a Sentry issue event, a PagerDuty incident raised or resolved. - Webhook gives the automation a private HTTP endpoint. Save it once to generate the URL and an API key, then anything that can POST can start a run, which is the escape hatch for every tool not in the list. ![image]()Drilling into a Linear trigger, which can fire when an issue is created or its status changes. ### Design for Duplicates and Failures Events repeat. A pull request can receive several pushes, an issue can change status twice, and a webhook sender can retry after a timeout. Make the first instruction idempotent where possible: ask the automation to update one named comment, label an item only when the label is absent, or report a duplicate instead of opening another ticket. Also decide where a failed run becomes visible. A weekly report that silently fails is worse than no report because people assume it happened. Send a failure notice to the same channel that receives the successful output, and include the run link or identifier needed to inspect the cause. ## What Can an Automation Produce? A Cursor Automation can return a report, send a message, comment on a pull request, or propose code changes, depending on its repositories and enabled tools. Configure the destination explicitly so the result reaches a person who can use it. ![image]()What an automation can do back, from Slack and Teams messages to GitHub pull-request actions and any MCP server you connect. The native actions cover common handoffs: send a Slack or Teams message, read a channel, and comment on a GitHub pull request, open one, or request reviewers. Everything else rides on MCP, with built-in choices for services such as Linear, Vercel, Cloudflare, Convex, and Clerk. Memories can carry context between runs. Repo-backed runs work on agent branches and can push or [open pull requests by default](https://cursor.com/changelog/06-18-26), so branch protection and human review remain the merge boundary. ## Templates The marketplace ships ready-made automations grouped by job. Dashboard categories cover code review, security, incident triage, and data research. Named starters include finding critical bugs, scanning for vulnerabilities, generating docs, and adding test coverage. The no-repo additions from May include a Slack digest that summarises morning messages by priority and an analytics agent that pulls weekly metrics. Add one, read its instructions, and tune it to your stack before enabling it. ## Automated Code Review The review and security templates are a practical place to start because the event, output, and reviewer are clear. A pull-request trigger can leave a non-blocking comment before anyone decides whether the findings are useful enough to become a team gate. ![image]()[Matthew Lake](/about/matthew-lake/) > [BugBot](/ai-code-review/bugbot/) is the turnkey review path, while an automation is the build-it-yourself path. Point an automation at pull-request opened and pushed, give it narrow review instructions, and let it return a comment or report. You control what it checks and where results go; the trade-off is that you also own calibration, permissions, and noise. Start with one repeated concern that a general reviewer does not cover, such as checking a release note, a documentation change, or a project-specific convention. Keep findings advisory until a sample of real pull requests shows that the agent has a useful signal. The deeper setup for review tooling lives in the [AI code review guide](/ai-code-review/). ## How Is Automation Usage Billed? Cursor Automations consume cloud-agent usage. A run configured as Me charges your usage; a Service account uses the team pool and requires an administrator to configure it. Treat every recurring trigger as a budgeted system. Cursor's [current pricing page](https://cursor.com/pricing) lists included usage and on-demand billing terms; the rate can differ by plan, model, and workspace settings. Set a spending limit before enabling cloud agents and inspect the [Automations documentation](https://cursor.com/docs/cloud-agent/automations) before scaling a schedule. Billing ownership, token rates, trial discounts, and included usage can change; date any internal budget decision to the plan page you checked. ## Limits and Gotchas - Runs use the maximum supported context window , without a context-size toggle. Set a cap and watch the first week. - Scheduled runs can lag under load. They will not run early, but do not build a hard real-time dependency on the second. - Fork pull requests are unsupported except on merge, and only public Slack channels are visible to Slack triggers. - No Repository removes the built-in code workflow. MCP and other connected tools may still read or write external resources; their permissions need a separate review. - Repo runs use agent branches. They can push and open pull requests by default; branch protection and human review still decide what merges. Keep the first production version deliberately dull. One trigger, one source, one destination, one owner, and one written stop condition are enough. Add MCP connections, external writes, and parallel schedules only after the simple run has produced reliable evidence for several cycles. ## When Automation Earns Trust Cursor Automations are most valuable for small, repeatable jobs with a visible owner and output: a pull-request check, scheduled report, incident summary, or issue triage step. Begin with reporting and strict limits, then allow more actions only after checking how it performs. Keep human review responsible for the merge while the automation reduces a specific queue of work. ## FAQ ### What do Automations cost? Cursor Automations are billed through cloud-agent usage rather than a single fixed price per automation. The exact rate, included amount, and overage policy can change, so check Cursor's current pricing page before you turn on a recurring trigger. Set a limit first, then measure a bounded trial before relying on an automation every day. ### Do I need a repo? Cursor Automations do not require a repository for tasks such as digests, alerts, or research; select No Repository for those tasks. That disables repository cloning and the built-in code-editing and pull-request workflow, but connected tools may still access external resources. No Repository does not make every tool read-only. ### Can they review PRs? Cursor Automations can review pull requests when a pull-request trigger and the required GitHub actions are configured. Start with one repository and a non-blocking comment workflow, then inspect false positives, missed issues, run cost, and permissions before treating the output as a required gate. ### How should I test an automation? Developers should test a Cursor Automation with a manual trigger or a narrow event on a disposable branch. Give it read-only instructions or a reporting task, check the run log and output location, then add a single write action if the result is reliable. Test failure, duplicate-event, and permission paths before scheduling it. ## Related - [Cursor guide](/ai-coding-tools/cursor/) for the editor and pricing it all sits on. - [Best AI code review tools](/ai-code-review/) for BugBot and the wider review landscape. - [Cursor vs OpenCode](/ai-coding-tools/comparisons/cursor-vs-opencode/) for how the cloud-agent and automation story compares with the open-source side. --- ## Cursor Context & Rules URL: /ai-coding-tools/cursor/context-and-rules/ Cluster: AI Coding Tools > Cursor [Cursor](/ai-coding-tools/cursor/) context is the evidence an agent uses for a task: files, selected code, errors, documentation, and conversation history. Cursor rules are reusable instructions that tell the agent how your project should be changed. Select context for today's problem and commit rules for conventions that should survive the next chat. My Cursor work centers on cloud-agent verification, where a short committed rule outlives the chat and gives the reviewer something stable to inspect. I want the agent to find the right implementation and follow the project's conventions without attaching the whole repository to every request. This guide assumes you are configuring Cursor. If you are still choosing an assistant, start with our [AI coding tools recommendations](/ai-coding-tools/). Once the rules are in place, test them with a small task from the [Cursor workflow guide](/ai-coding-tools/cursor/workflows/) before using them in [scheduled or triggered automations](/ai-coding-tools/cursor/automations/). ## What Is the Difference Between Context and Rules? In Cursor, context supplies task-specific evidence; rules supply repeatable project guidance. Neither guarantees that the agent will choose the correct implementation. More context does not automatically produce a better answer. A whole repository can contain several approaches to the same job, abandoned components, generated output, and tests that intentionally break rules. When Cursor receives all of it, the agent has more material to combine and more ways to choose the wrong pattern. Start by asking what evidence the task needs. A request to change a validation message may need the form component, the schema, and one test. A request to explain why a deployment failed may need pasted terminal output, the deployment configuration, and the changed workflow. Let the question determine the context instead of attaching everything available. | Question | Start With | Add Only If Needed | | Why does this function fail? | Function and error | Caller and test | | Where is this behaviour defined? | Codebase search | Relevant folder | | How does this API work? | Official documentation | Local wrapper code | | What changed this regression? | Current diff and test | Git history | That approach also protects private material. Do not attach secrets, production exports, or customer data just because an agent can read it. Redact the failing value, use a minimal reproduction, or ask for a diagnostic plan before providing more detail. Context is an engineering choice and a data-handling choice at the same time. ## Choose the Smallest Useful Context Work outward from the code under discussion. Select the current function or component first, then add the test that proves its intended behaviour. Add a parent or caller only if the local code does not explain the contract. This gives Cursor a coherent slice of the system and makes it easier for you to notice when the answer relies on something outside that slice. For example, a request to add a loading state can start with the component, its data hook, and the closest test. If Cursor finds that the hook is shared by several screens, ask it to list those consumers before editing them. That list turns an implicit expansion into a decision you can make. ### Read the Context List Before Sending The context picker is part of the prompt. Confirm that a selected folder did not pull in generated files, a similarly named old feature, or a private configuration file. If the prompt contains a large codebase search result, ask Cursor to identify the files it relied on. A short answer with cited paths is easier to verify than a broad summary. ## Use `@` Symbols with an Intent Cursor's `@` references are shortcuts for explicit code context. The [context guide](https://cursor.com/learn/context) explains how selected files and other evidence help Agent understand a task. The exact picker can vary by surface and version, so choose the smallest reference that matches the question. | Reference | Supplies | Best Use | | `@code` | One code symbol | Known function or type | | `@file` | One source file | Targeted code change | | `@folder` | Bounded directory | One feature area | | Agent search | Relevant codebase paths | Unknown implementation | | Pasted output | Selected terminal text | Build or test failure | When terminal output is the evidence, paste the command with the smallest relevant failure block. Remove tokens, customer data, and unrelated logs before sending it. Use web context cautiously for a current vendor detail, then check the primary source yourself before taking action. A search result can lead to an obsolete documentation page or a third-party explanation that does not match your installed version. For dependency, API, or price changes, attach the official documentation page and record the date you checked it. ## Write Rules That Change Behaviour A rule file records decisions that help the agent avoid recurring mistakes. Good rules name a tool, file location, command, ownership boundary, or prohibition. Remove vague instructions such as "write clean code." A rule such as "use pnpm, never npm" names behaviour a reviewer can test. Cursor's [rules documentation](https://cursor.com/docs/rules) remains the source of truth when rule modes or file formats change. Save this example as `.cursor/rules/frontend.mdc`. Cursor project rules require the `.mdc` extension; a plain Markdown file in that directory is not a project rule. ```markdown --- description: Frontend conventions globs: "src/components/**/*.tsx" alwaysApply: false --- # Component Rules - Use TypeScript and the existing component patterns. - Keep tests next to the component when a test already exists there. - Use pnpm for project commands. - Do not add a dependency without approval. - Do not change public props unless the task names the migration. ``` Each line should survive a change of ticket. Stack choices, test commands, formatting conventions, security boundaries, and folder ownership are useful. A temporary design decision, a customer name, or the acceptance criteria for one issue belongs in the task prompt instead. If a rule stops being true, change or remove it rather than letting a stale instruction quietly win. ### Write Constraints Before Preferences Put non-negotiable constraints near the top: do not expose secrets, do not bypass authorization, do not install packages, and do not make destructive database changes without approval. Preferences such as naming style matter too, but an agent should never mistake a style request for a safety boundary. Keep rules readable enough for a developer to challenge them in review. An instruction that cannot explain why it exists will accumulate exceptions. The best rule files act as short onboarding documents and as a record of decisions already paid for by the team. ## Scope Rules to the Code They Govern Choose a rule mode to control when Cursor includes its instructions. Always Apply uses `alwaysApply: true`; Apply to Specific Files uses matching `globs`; Apply Intelligently gives the agent a description it can use to select the rule; Apply Manually requires an explicit reference. Test the selected mode on a matching task. Cursor also supports plain `AGENTS.md` instructions at the project root and in nested directories. Keep equivalent instructions consistent if your repository uses both formats. One global rule file is tempting, but it becomes noisy when a repository contains an application, infrastructure, documentation, and scripts with different conventions. Place a general project rule at the root, then add scoped rules where a local area needs extra instructions. A database folder can state migration rules without forcing that material into every UI request. Keep overlapping rules consistent. If a local file says a convention differs from the project default, state the exception plainly. Conflicting instructions make it difficult to tell whether Cursor ignored a rule or selected the wrong one. Test a scoped rule with a small request in the matching path before relying on it for a larger change. - Use a root rule for package manager, tests, security, and repository-wide boundaries. - Use a folder rule for framework or ownership details that only apply there. - Use a task prompt for the work that must happen today. - Delete a duplicated legacy rule once the new rule has proved itself. ## Debug a Rule Before Trusting It Test a new rule before a production migration depends on it. Make a harmless request that should visibly follow the rule. For example, ask Cursor to create a small component in the scoped folder and verify that it uses the expected test command, styling system, and file pattern. If the response ignores a requirement, check the file path, frontmatter, scope, and current documentation. Then inspect the generated diff. A model can repeat a rule in its explanation and still miss it in code. The practical test is whether the changed files, command, and imports obey the convention. Rules guide the agent; they do not enforce file-access restrictions or replace code review, command permissions, or automated checks. A rule that says "do not read secrets" is not a security control that blocks those reads. ## Keep Task Context Out of Project Rules Rules become less useful when they turn into an issue tracker. "Use the blue button from the current mockup" and "fix ticket ABC-123" make no sense to the next person. Put that information in the conversation, along with the exact files and checks for the task. The rule file should describe the project after the ticket is closed. This separation makes agent sessions shorter. A new chat can begin with the immediate objective and a few selected files, while the agent already has the stable conventions from the repository. It also makes review simpler because durable instructions change in their own diff rather than slipping into an unrelated feature. ## What to Keep in Context Use `@` to select files and other information relevant to the task, and use committed rules for conventions that should outlive it. I would test one scoped rule on a small change before adding a repository-wide rule set. The changed files tell you more than the agent's assurance that it followed the instructions. ## FAQ ### What goes in `.cursor/rules`? Cursor project rules should contain stable project conventions in `.cursor/rules`: language and framework choices, test commands, file patterns, security boundaries, and changes the agent must not make. Keep short-lived ticket details and one-off layout instructions in the chat. A rule should still make sense when the next developer opens the repository. ### How do I reference files with `@`? Developers can reference files in Cursor by typing @ in a conversation and selecting the smallest source that can answer the task. Use @code for a known symbol, @file for one source file, and @folder when a feature spans a directory. Ask the agent to search the codebase when you do not know the implementation. Check the included context before relying on the response. ### Do rules apply when Agent uses Composer? Cursor project rules guide Agent when their scope and application mode match the task, including when Agent uses a Composer model. Composer is the model; Agent is the workflow that reads files and uses tools. Rules do not apply to Cursor Tab, and they are not an enforced security boundary. ### Should I keep a `.cursorrules` file? Developers should use `.cursor/rules` files for new Cursor configurations because Cursor documents them as the current rules system. A root `.cursorrules` file may remain while you migrate, but avoid maintaining conflicting copies. Move one convention at a time, test it on a small request, then remove the duplicated legacy instruction. --- ## Grok Bot Explained: The Cloud AI Teammates Included with Cursor Plans URL: /ai-coding-tools/cursor/grok-bot/ Cluster: AI Coding Tools > Cursor Grok Bot is a desktop and mobile app from Cursor and xAI that gives you persistent AI teammates for browser, file, and connected-app work. Your Bots share a cloud computer that keeps working while your laptop is closed. Each Bot has its own instructions and conversation, but its files and signed-in browser sessions are shared with your other Bots. I see its strongest role in the work around coding: reproducing bugs, checking vendor dashboards, collecting research, and handling recurring chores. Access is bundled with qualifying subscriptions, including the current Premium, Super, and Ultra plans; it is not included with every paid plan. ## What Is Grok Bot? Grok Bot combines persistent conversations with a cloud computer and tools that let an agent act on a task. xAI launched the product in beta on August 11, 2026. A Bot is a durable teammate with a name, a job, its own conversation and its own working context, and it can sign in to your tools and use them the way you would. You talk to it in a message thread on the desktop or mobile app, the same way you'd message a colleague. You can have roughly 50 Bots on one account, put 2 to 6 Bots in a group chat, and let Bots hand work to each other while you're away. Skills are reusable instruction sets shared across Bots, and routines run one Bot on a schedule or on an event such as a Slack message or a GitHub notification. A Bot can also watch you do a browser workflow once, up to 10 minutes, and draft a skill from it. My take so far is that the product is aimed at the work around your code rather than the code itself. Examples include a bug reproduction Bot that turns reports into reliable reproduction packs and a website builder that builds, buys the domain and deploys. Team Bots let a whole team work with a shared Bot. That is separate from giving your own Bots different roles or putting them in a group chat. ## Which Jobs Should You Give Grok Bot? I would give Grok Bot browser and inbox work, including bug reproduction, vendor-dashboard checks, research, and scheduled chores. I would keep repository edits, tests, and pull requests with a dedicated coding agent such as [Claude Code](/ai-coding-tools/claude-code/), [Codex](/ai-coding-tools/codex/), or the Cloud Agents that ship with [Cursor](/ai-coding-tools/cursor/) itself. Delegation is one way to connect the two workflows. Cursor's [Teams docs](https://cursor.com/docs/grok-bot/teams) say a Bot can hand a coding task to Cursor Cloud Agents under your existing team controls, and that toggle defaults to on. So a routine that watches a GitHub notification can triage the issue on the cloud computer and then spawn a Cloud Agent to write the fix. | Tool | Where the Agent Runs | What It Hands Back | | Grok Bot | One persistent cloud VM per user, shared by every Bot | Finished work inside the actual apps, or a delegated Cloud Agent task | | Cursor Cloud Agents | Isolated Cursor VM with the repo cloned | Proposed pull requests with screenshots and logs to review | | Claude Code | Your terminal, IDE or desktop, plus cloud routines | Local edits, commits and pull requests | | Codex cloud | Isolated cloud environment per repo | A diff, then a pull request when you ask for one | My take is that Grok Bot competes with your inbox and your browser tabs, and the coding agents compete with each other. I'd keep Claude Code for anything that touches a checkout, and give the Bot the reproduction packs, the vendor dashboards and the scheduled chores that eat a Monday morning. ## How Does Grok Bot’s Cloud Computer Work? Grok Bot runs your Bots on one persistent cloud computer per account, with a browser, terminal, and shared files. xAI's [overview page](https://docs.x.ai/grok-bot/overview) describes this shared workspace, and Cursor's Teams docs call it a dedicated Firecracker microVM. It includes a durable `/workspace` folder for files you want to keep. Bot computers run in Cursor's cloud infrastructure, which is the second half of the Cursor tie. Browser cookies and signed-in sessions are shared across the computer, so signing in for one Bot makes that session available to your other Bots. Between users the isolation is strict, with each user's computer dedicated and hardware-isolated. Inside your own account, though, a login or a file placed on that machine is visible to every Bot you create. - The computer stays on when you close the Grok Bot app or shut the laptop. - Durable project files belong in `/workspace`, and everything else is replaceable. - Update, Recover and Reset are the three recovery options, and a reset can discard unsaved work. - Save each Bot's instructions somewhere outside the computer too, so a reset never costs you the setup. Web traffic normally leaves from the cloud computer. The [Route egress through this desktop](https://docs.x.ai/grok-bot/settings-and-notifications) setting sends it through your own desktop instead, so sites see your desktop's IP address and the Bot can reach networks that desktop can reach. It applies to one desktop at a time, and an Enterprise admin can switch it off for the whole team. ## Which Plans Include Grok Bot? On the current X Subscription, Premium, Super, and Ultra include Grok Bot; Free and Plus do not. Legacy paid Cursor plans and qualifying linked Grok accounts have separate eligibility rules. Check the [plans and billing guide](https://cursor.com/help/grok-bot/plans) for your account before subscribing. The distinction also affects usage. Current plans share a weekly pool across Cursor, Grok, and Grok Bot, with prepaid credits for additional usage. Legacy accounts retain weekly Grok Bot allowances and optional on-demand billing. Linked legacy grants do not stack with a Cursor allowance. The [AI coding tools ranking](/ai-coding-tools/) ranks Claude Code first and Cursor second. The Grok Bot bundle adds useful work beyond Cursor’s editor. I still value that combination, but the useful question is whether you will use the Bot enough to justify the eligible plan. The [Cursor guide](/ai-coding-tools/cursor/) covers current plan prices. ## What Access Does Grok Bot Have to Your Computer? Grok Bot can access local commands and files only through its separately configured local-computer access; by default, local execution asks for approval each time. Installing its desktop app does not give it unrestricted control of your laptop or make it another Cursor Agent mode. - Local-command execution is a separate permission: Ask every time is the default, with Always allow and Never allow alternatives where team policy permits. - Grok Bot requires data storage and does not support Legacy Privacy Mode. I would keep local execution disabled until a specific job needs it. ## Start a First Bot Job Install the desktop app for macOS, Windows or Linux, sign in with the Cursor account you already pay for, and create one Bot with a short name and one job. Send a first message that names the outcome, the sources, the constraints and the point where you want it to stop for review. The prompt below is the bug reproduction job from xAI's own use-case list, trimmed to my shape. You can dictate a message, start a live voice chat on desktop, iPhone and Android, and play voice memos a Bot sends back. In my testing each Bot can also have its own voice, which makes a roster of Bots easier to tell apart on a call. ```text Take the three newest bug reports in our Linear triage view. For each one, reproduce it in the staging app in your browser and record the exact steps, the expected result and what actually happened. Save one markdown file per bug in /workspace/repro/ with screenshots attached. Do not comment on the Linear issues or open any pull request. Stop and message me when the three files are ready. ``` You can attach up to 6 files per message on desktop, with documents capped at 25 MB and video at 200 MB. Type `/` to pick a skill and `@` to pull another Bot into the thread, and use Stop now if the job drifts. When the Bot hits a password, a 2FA prompt or a CAPTCHA, xAI's guidance is that the Bot should hand you control of the computer, so complete only that step and hand control back. ## Keep Usage and Approvals Visible When Grok Bot prepares an email or Slack message through its draft workflow, the conversation shows an editable draft with Send and Discard buttons. Other tool and browser actions follow their configured approval rules. Check the usage screen before a long job and after the first few runs. The account's plan determines whether additional work uses prepaid credits or legacy on-demand billing. Set an additional-spend limit and inspect actual consumption before scheduling overnight work. xAI tells you to keep sending messages, publishing, purchases and transfers, deleting or overwriting data, permission changes, production changes and legal terms behind explicit approval boundaries you set for each Bot, and its Auto Review can enforce them. Desktop offers Allow once, Deny or Always allow, and the mobile app lets you approve or deny a step when you are away from your desk. Auto Review rules can force an approval or always-allow a class of action for a Bot. One line from xAI's security page is worth pinning above your desk. An approval controls the proposed action, and it does not reverse work already completed. Write the boundary into the Bot's instructions as well, so a later routine can't widen the job while you're asleep. ## FAQ ### Is Grok Bot part of the Cursor editor? Grok Bot is a separate desktop and mobile app. It shares account and service connections with Cursor, while its Bots normally work on a persistent cloud computer. ### Does Grok Bot run on my own computer? Grok Bot agents normally run in the cloud. Local commands require a separate execution setting, whose default is Ask every time. Team policy can restrict the available choices. ### Do all my Bots share one cloud computer? Every Grok Bot agent on your account uses the same computer, including its files, browser cookies and signed-in sessions. Between different users the isolation is strict and hardware-level. xAI's computer docs say separate Bots are not separate security boundaries. ### Does Grok Bot eat my Cursor or Grok usage? Grok Bot shares weekly usage with Cursor and Grok on the current X Subscription. Legacy Cursor plans and account links follow separate allowance and billing rules. ### Is there a Linux desktop or Android app? Yes, the Grok Bot desktop app now runs on Linux alongside macOS and Windows. [xAI's mobile guide](https://docs.x.ai/grok-bot/mobile) covers iPhone and iPad on iOS or iPadOS 18 or later, and Android 9 or later, and links both app stores. Cursor's overview still lists only iPhone, so use the current download links for your device. ### Can a Bot write code in my repo? A Grok Bot agent can use its cloud terminal or delegate a coding task to Cursor Cloud Agents when access is configured. I prefer a dedicated coding agent for repository changes because the diff, tests, and review are easier to follow. Any proposed pull request still needs review. ### Is there a free trial for Grok Bot? Grok Bot has a free trial that Cursor's plans page describes as a usage credit inside a 7-day window. A long first job can burn most of that credit in one go, so give the first Bot a small, clearly defined job before you let it run overnight. ### Which model does Grok Bot use for its Bots? Cursor selects Grok Bot’s models on your behalf, and the Teams docs say there is no customer-facing model picker. You choose the Bot's name, job and instructions, and the model behind it is not something you configure today. ## Where to Go Next - [Best AI coding tools](/ai-coding-tools/): our overall ranking, with Claude Code first and Cursor second. - [Cursor guide](/ai-coding-tools/cursor/): the editor, the models and the plan names your Grok Bot access hangs off. - [Cursor Origin hosted codebases](/ai-coding-tools/cursor/origin/): the option when a job needs a Cursor-hosted remote rather than a Bot. - [Cursor Automations](/ai-coding-tools/cursor/automations/): scheduled cloud agents that stay inside Cursor and hand back pull requests. Plan eligibility, usage metering, platform support and computer limits were checked against the cited xAI and Cursor pages on October 16, 2026. --- ## Cursor Origin Hosted Codebases URL: /ai-coding-tools/cursor/origin/ Cluster: AI Coding Tools > Cursor Cursor Origin hosts Git repositories and pull requests at [cursor.com/codebase](https://cursor.com/codebase). You can create a repository with Origin as its main remote, or mirror an existing GitHub repository while GitHub remains the source of truth. I would start with a new side project or a mirror before moving a team's code hosting. The important choice is where pushes, reviews, and CI actually happen. The [Cursor editor](/ai-coding-tools/cursor/) can work with local files regardless of which service hosts their Git remote. ## What Is Cursor Origin? Origin is Cursor's Git hosting service, currently in early beta. It provides repository storage, code browsing, pull requests, integrations, and access for cloud agents. Its name is separate from `origin`, the conventional name of a Git remote that can point to any hosting service. You claim a codebase namespace during setup; it cannot currently be renamed during the beta. That name becomes part of the URL, in the shape `cursor.com/codebase/acme-corp`. Codebase settings apply across the team's Origin repos. A repo still has its own Settings tab for sync status, access, and apps. - Open the Codebase tab, choose New, and name the repo. - Install the Origin CLI if you want the documented clone and push commands. - Icons next to each repo name show whether Cursor hosts it or GitHub is the source. ## Host a Repo on Origin For a repo created on Origin, Origin is the source of truth. Pushes land on Origin, and GitHub stays off that path. Cursor documents standard git over HTTPS at `https://origin.cursor.com/{owner}/{repo}.git`. Use the clone command for a new checkout. The alternative `git remote add` example is for an existing local project with no remote named `origin`. Replace the example URL with the repository you created; do not run both alternatives in the cloned directory. ```bash origin auth login git clone https://origin.cursor.com/acme/checkout.git # Or add the remote to a local project git remote add origin https://origin.cursor.com/acme/checkout.git git push -u origin main ``` The same docs offer a dual-push remote while you evaluate GitHub and Origin in parallel. For a full history copy that should keep GitHub as source of truth, use the mirror flow instead of a second push URL. I would host a new side project on Origin first, because the remote, the pull request, and the Cursor agent sit in one place. I would not move a team GitHub org until someone has cloned, pushed, and merged one real change on a disposable repo. ## How Does Origin Mirror a GitHub Repository? Cursor Origin mirrors GitHub history and updates, while ordinary pushes still reach GitHub first. Use it when the code already lives on GitHub and you want Origin browse, search, and pull requests on that history. Cursor's [mirror docs](https://cursor.com/docs/origin/mirror-github) require the Cursor GitHub app plus GitHub admin access on the source repo. | Repo Kind | Source of Truth | GitHub's Role | | Origin-hosted | Origin | Not on the push path | | GitHub mirror | GitHub | Pushes still land there | | Detached copy | Origin after detach | Unchanged, no longer synced | A mirror includes git history, branches, tags, and pull requests. It does not include GitHub Issues, GitHub Actions workflows, or secrets. If you only want review comments on GitHub pull requests, [BugBot](/ai-code-review/bugbot/) does that without moving storage. Detach from GitHub under Settings, then General, then Danger Zone. That stops the sync and turns the Origin copy into a standalone hosted repo. The original GitHub repository stays in place after detach. ## Review and Ship from Origin Every Origin repo includes a pull-request list. Open the Pull Requests tab to see the list, then open a row for activity, commits, checks, and files changed. You can request reviewers, comment on lines, and merge once reviews and CI are satisfied. Cursor cloud agents can also open pull requests on Origin as part of a task. - Origin-hosted pull requests stay on Origin through comment, review, and merge. - Mirrored pull requests sync both ways with GitHub comments and replies. - Stay on GitHub Issues and Actions when those are still the team's source of work. On a mirrored repo, pull requests sync both ways. Comment in Cursor and it posts to GitHub. Reply on GitHub and it shows up on Origin. Pull requests opened on a repo created directly on Origin stay on Origin. Apps such as Vercel, Depot, and Buildkite install at the codebase level. Vercel can attach a preview to a pull request. Cursor's settings docs say Depot and Buildkite run on Origin-hosted repositories. Mirrored repos keep their CI running on GitHub. ## The Smallest Hosting Move Host a new repo on Origin when Cursor should be the remote and the team can live without GitHub Issues and Actions on that project. Mirror an existing GitHub repo when the team still needs GitHub as the source of truth and only wants browse, search, and two-way pull requests inside Cursor. Skip a wholesale org migration until one hosted repo and one mirrored repo have each survived a real push, review, and merge. Origin is still in early beta, so admins can disable it, and the permission UI can still change. ## FAQ ### Is Origin another Cursor editor feature? Origin is Cursor’s Git hosting service, separate from the editor’s local editing features. The Cursor editor handles your local editing workflow; Origin stores repositories and provides browser-based code and pull-request views. ### Who can use Origin today? Cursor Origin is available on Premium, Super, Ultra, Teams, and Enterprise, plus eligible legacy paid Cursor plans. Free and Plus do not include it. Team administrators can disable access, and Legacy Privacy Mode is unsupported. Check [Origin access requirements](https://cursor.com/docs/origin) before setup. ### Does a GitHub mirror replace GitHub? A Cursor Origin mirror does not replace GitHub: it keeps GitHub as the source of truth unless you detach it to create a standalone Origin repository. Pushes pass through to GitHub, and Origin mirrors the result. Detach from GitHub only when you want the Origin copy to become a standalone hosted repo. ### What does a mirror leave on GitHub? A Cursor Origin mirror leaves GitHub Issues, GitHub Actions workflows, and secrets on GitHub. It copies Git history, branches, tags, and pull requests. Depot and Buildkite CI on Origin apply to Origin-hosted repositories, not mirrored repositories. ### Can I review GitHub pull requests in Cursor? You can review GitHub pull requests through Cursor Origin when the repository is mirrored and the pull requests are synced. A comment in Cursor posts to GitHub, and a GitHub reply shows up on Origin. Pull requests opened on a repo created directly on Origin stay on Origin. ### How do I clone an Origin repo? Developers can clone a Cursor Origin repository by opening it at cursor.com/codebase, copying the HTTPS URL from the Code menu, and cloning `https://origin.cursor.com/{owner}/{repo}.git`. Sign in with `origin auth login` before the first git operation if the clone or push fails. ## Where to Go Next - [Cursor guide](/ai-coding-tools/cursor/) for the editor this hosting surface sits beside. - [Best AI coding tools](/ai-coding-tools/) for how Cursor ranks against the other tools. - [Grok Bot works next to Cursor](/ai-coding-tools/cursor/grok-bot/) for the cloud teammate that can leave the editor. - [Cursor Automations](/ai-coding-tools/cursor/automations/) for scheduled agents that already run on Cursor cloud. Hosting, mirror, and pull-request facts were checked against the cited Cursor Origin pages on October 16, 2026. --- ## Cursor Workflows and VS Code Migration URL: /ai-coding-tools/cursor/workflows/ Cluster: AI Coding Tools > Cursor A useful [Cursor](/ai-coding-tools/cursor/) workflow follows six steps: define one outcome, supply relevant context, inspect the plan, review the diff, run the appropriate checks, and accept or revise the result. Cursor Agent can coordinate edits and commands, but you still need evidence that its changes solve the requested problem. My Cursor work is mostly cloud-agent verification against deployed development branches, so I want that evidence to survive after the agent session closes. The routine below keeps a local fix from becoming an unplanned redesign and gives VS Code users a practical way to try the editor. Our [AI coding tools guide](/ai-coding-tools/) explains why we recommend Cursor; the workflows here show how to turn that choice into work you can review. If the main question is whether to keep your existing editor, read [Cursor vs VS Code](/ai-coding-tools/comparisons/cursor-vs-vscode/) before changing the rest of your setup. ## How Should You Structure a Cursor Session? Developers should structure a Cursor session around one specific change: describe the desired change, name the constraints, let the agent inspect the relevant files, review the proposed change, run the smallest useful check, then decide whether to keep it. Check the changed files and test results rather than relying on the agent saying it has finished. | Stage | Your Job | Evidence | | Frame | Name outcome and boundaries | Testable prompt | | Inspect | Check files and plan | Relevant paths only | | Edit | Read the diff | Expected code changes | | Check | Run a narrow test | Command output | | Decide | Accept, revise, or reset | Reviewable change | A focused change can touch a component, a test, and a type definition without going beyond what you asked for. "Make the existing email field required and update its validation test" is reviewable. "Make the account screen better" leaves room to change layout, copy, state handling, and interfaces at once. ## Start with a Specific Request Give Cursor the outcome, the place to work, and the things it must leave alone. Mention the expected check before the agent writes code. That turns the prompt into a small contract instead of a vague brief. ```text In src/components/ProfileForm.tsx: - make the existing email field required - keep the existing Tailwind classes and form library - update the existing validation test only - do not change the API payload or install packages Run the targeted form test and show the diff before I accept it. ``` The file path reduces the search space. "Do not change the API payload" stops a plausible but unwanted expansion. Naming the test gives the agent a finish line that is stronger than "make sure it works." If you do not know the exact file, ask Cursor to locate it first and return a plan with paths before asking for edits. Keep discovery and editing separate for unfamiliar code. Start with a read-only request such as "find where password-reset errors are rendered; list the files and explain the current flow." Once you understand the map, make the edit request. This avoids accepting a change built on the agent's first guess about the architecture. ### Name the Non-Goals Non-goals save more cleanup than broad positive instructions. Say that a request must not rename public functions, alter a database schema, update dependencies, or reformat unrelated files. A coding agent often sees nearby improvements. Your prompt has to tell it which improvements are not part of today's job. - Use one acceptance condition, such as a test, visible behaviour, or expected output. - List files or folders that are in scope when you know them. - Say which interfaces, migrations, or dependencies must stay unchanged. - Ask for a plan first when the change crosses more than one feature area. ## Inspect the Plan and Diff Read the file list before you read every line of generated code. A small UI request that reaches authentication, environment configuration, or unrelated shared components deserves a pause. The agent may have found a genuine dependency, but it may also be following an assumption that needs correcting before the diff grows. Then compare the diff to the request in two passes. First, look for scope: are these the expected files and does the amount of change match the task? Second, look for behaviour: did the validation, error state, types, and tests change together in a way that makes sense? A passing test is evidence, but the diff still has to answer the original request. Cursor makes this practical because you can reject one part of a diff rather than choosing between accepting everything and throwing the whole attempt away. If one piece is right, retain it and give a narrow correction. If the plan was wrong, reject it, state the missing constraint, and restart from the clean state. ### Ask for an Explanation When the Diff Is Wide Ask a direct question when the agent changes a file you did not expect: "Why does this task need the shared request client?" or "Which test fails without this migration?" The answer should point to a concrete call path, type, or failing check. If it cannot, narrow the work back to the requested behaviour. Do not use an agent's prose explanation as a replacement for the diff. It can describe an intended result while the code does something else. The changed lines and the command output remain the objects you approve. ## Use the Terminal as Evidence Cursor can carry terminal output into the conversation, which removes the tedious copy and paste after a failed check. That is a good use of the terminal integration. The command is still an instruction to your machine, however, so treat it as code you are about to run. Start with checks that have a narrow blast radius: a focused unit test, a type check, a build, or a read-only Git status command. Read the command before approval, including its working directory, flags, and any redirected output. A command that deletes generated files, resets a database, publishes a package, or modifies credentials needs a human decision outside the agent loop. ```bash # Inspect the working tree git status --short # Run the targeted test (adapt to your project scripts) pnpm test -- ProfileForm # Only after reading the diff and test output pnpm build ``` The test and build commands above are examples for a project with those pnpm scripts. Tests and builds execute project code and can create files; they are not read-only inspection commands. When a check fails, give Cursor the failure output and ask for a diagnosis before asking for a fix. "Explain the first failure and propose the smallest repair" keeps it from treating every warning as a reason to rewrite the module. If the failure looks unrelated to the change, record it and do not let the agent bury it with a broad cleanup. ## When Should You Start a New Cursor Chat? Developers should start a new Cursor chat when the task changes or Cursor keeps relying on an assumption you have corrected. Long chats collect old file names, rejected ideas, and instructions that only applied to the first task. That context can make Cursor confident about a problem you already solved. Starting a fresh conversation replaces stale context with a short, accurate brief. Reset when the agent repeats an instruction you corrected, changes files outside the current task, or starts solving a different problem from the one in front of you. Put the goal, relevant paths, constraint, and expected check in the first message. Add one or two files with `@` when their local conventions matter, then use [Cursor context and rules](/ai-coding-tools/cursor/context-and-rules/) for durable project guidance. A project rule says "use pnpm" or "keep tests beside source files." A task instruction says "make the existing email field required." Put the first kind in a committed rule file and the second in the conversation. Mixing durable rules with temporary tasks makes both harder to maintain and follow correctly. ## Use a Second Plan for High-Risk Work Use a second agent while a risky change is still a file-level plan. For a migration, access-control change, payment flow, deployment change, or cross-repository feature, ask Cursor for that plan. Ask a separate tool such as [Codex](/ai-coding-tools/codex/) to review the plan for missing tests, unsafe assumptions, and rollback needs. A second plan earns its cost when a wrong first plan could change data, expose an account, or consume a day of repair work. Two independent plans often agree on the main route while exposing one overlooked boundary. That disagreement is the review result. | Task | First Move | Second Review? | | Copy change | One focused edit | No | | Component feature | Plan, diff, targeted test | Sometimes | | Data migration | Written rollout plan | Yes | | Access control | Threat and test plan | Yes | ## Can You Try Cursor Without Leaving VS Code? Developers can try Cursor without leaving VS Code by keeping both editors installed and opening the same Git repository in Cursor for a limited trial. Cursor keeps a VS Code-style editor, which makes migration less about project conversion and more about checking that your usual tools still work. Your repository, Git history, terminal profiles, and `.vscode` settings remain where they are. Cursor can import settings, keybindings, snippets, themes, and many extensions, but "many" is not a guarantee for the extension you depend on most. Keep VS Code installed during the trial. Open the same non-critical project in both editors and test the workflow that would force you back: a debugger, remote session, language server, company extension, or Microsoft-provided tooling. Check that a commit made in one editor appears normally in the other. The trial is a compatibility test. Check Cursor's [keyboard shortcut reference](https://cursor.com/docs/reference/keyboard-shortcuts) before standardising a team workflow. ### Learn Cursor's AI Surfaces The productive adjustment is choosing the right Cursor surface. Use inline editing for a precise local change. Use chat to understand an unfamiliar block. Use a multi-file agent only when the task needs coordinated edits and command output. The file explorer and command palette should still feel familiar, so avoid relearning them before you have a real agent task to practise on. ## Common Workflow Failures The common failure is a vague prompt followed by a large, unreviewed acceptance. Cursor can make the work look fast because code appears quickly. The repair time arrives later when a hidden interface changed, a dependency slipped in, or a test only covered the new happy path. - Too much scope: split the request by visible outcome and review each diff. - Too much context: include the smallest relevant files, then widen only when needed. - Blind terminal approval: read every command and start with narrow checks. - Stale conversation: open a fresh chat with the current constraints. - Untested migration: keep VS Code beside Cursor until your critical tools prove they work. ## The Workflow That Holds Up Cursor is most useful when you check its work as it goes. Give it a clearly defined job, inspect the plan and diff, check the command results, and reset the conversation when it starts relying on outdated or irrelevant context. That routine keeps the speed of multi-file help without turning approval into a leap of faith. ## FAQ ### Should I let Cursor run terminal commands? Developers should let Cursor propose and run commands only when you can read the exact command and judge its scope. Start with read-only checks and normal test commands. Pause before migrations, deletion, publishing, credential changes, or any command whose output you would not know how to recover from. ### When should I start a new Cursor chat? Developers should start a new Cursor chat when the agent keeps carrying an old assumption, the task changes, or the suggested files become wider than the request. Put the current goal, the relevant files, the checks to run, and the constraints in the opening prompt. A short reset is usually cheaper than correcting a long conversation. ### Can I keep VS Code after installing Cursor? Developers can keep VS Code installed alongside Cursor because both editors can use the same project files and Git history. Keep VS Code installed during a trial, then test proprietary Microsoft extensions, remote development, debugging, and any work profile before changing your default editor. Nothing about installing Cursor requires removing VS Code. ### Is a second AI review worth the time? A second AI plan or code review is worth the time when a change crosses authentication, data migration, deployment, payments, or several modules. For a small local edit, it is usually slower than reading the diff yourself. The point is to expose a costly wrong assumption before the first agent writes a large change. --- ## Best AI Coding Tools for Solo and Indie Developers (2026) URL: /ai-coding-tools/guides/best-for-solo-devs/ Cluster: AI Coding Tools > Guides Cursor is our default recommendation for a solo developer who wants an AI editor. Claude Code and Codex suit delegated project work, while Copilot fits developers who want assistance inside an existing editor. Start with one tool and add a second only when it has a repeated job that justifies the bill. I run Claude Code and Codex as separate coding agents, and I also use Cursor cloud agents against deployed test sites. That setup grew from repeated jobs rather than a shopping list. One agent handles a clearly defined task, another may check the changes and test results, and I still decide whether the work passes. Our wider [best AI coding tools](/ai-coding-tools/) guide ranks Claude Code first overall. This solo-developer shortlist emphasizes a setup one person can afford and supervise, with Cursor my pick for developers who want a visual AI editor. The recommendations differ by workflow, rather than requiring every reader to buy the overall winner. If this is your first coding assistant, follow the [beginner setup and review walkthrough](/ai-coding-tools/guides/getting-started/) before paying for a second tool. ## Shortlist for Solo Developers | Tool | US monthly price | Main Interface | Main limit | | Cursor Premium | $30 | Cursor editor | Agent allowance | | Claude Pro | $20 | Claude Code | Session and weekly caps | | ChatGPT Plus | $20 | Codex and ChatGPT | Agent usage | | GitHub Copilot Pro | $10 | Current editor | Agent credits | ## Which AI Coding Tool Fits Your Solo Workflow? Choose Cursor for visual editing, Claude Code for repository investigation and command-driven work, Codex for delegated tasks across local and cloud workflows, or Copilot when keeping your existing editor is the priority. [Cursor Premium](/ai-coding-tools/cursor/) suits a solo developer with an editor-led day. Cursor places completion, chat, agent edits, cloud agents, and review controls inside one product. Premium includes cloud agents, automations, and Grok Bot; third-party models are billed separately. Plus costs $8 and has a narrower feature set. Check the account dashboard for usage. [Claude Code](/ai-coding-tools/claude-code/) suits developers who want an agent to inspect a repository and run commands through desktop, terminal, or supported editor workflows. Claude Pro includes Claude Code and the Claude web app for $20 per month in the United States. Anthropic states that Pro still has session and weekly usage limits, and API usage remains separately billed. [Codex](/ai-coding-tools/codex/) suits developers who want the same coding agent across desktop, command line, editor, and cloud tasks. ChatGPT Plus includes Codex, while use counts against the plan's agent allowance. OpenAI says larger repositories and longer-running jobs consume more than small scripts. [GitHub Copilot Pro](/ai-coding-tools/copilot/) costs $10 monthly and fits developers who want to keep their editor. It works inside editors you may already use and includes unlimited completions on paid plans. GitHub now meters agent and chat work through a monthly AI-credit allowance, so heavy agents can reach a limit even while normal completion keeps working. No paid plan removes the need for your own review. Copilot can suggest the wrong line, Cursor can spread an edit across too many files, Claude Code can follow an assumption you forgot to state, and Codex can spend its allowance on a task that lacked a clear finish line. Whichever tool you choose, you still need to check its work. These are current US monthly prices before tax. GitHub lists Copilot Pro at $10, [Cursor lists Plus at $8 and Premium at $30](https://cursor.com/pricing), and [Anthropic lists Claude Pro at $20](https://support.claude.com/en/articles/8325606-what-is-the-pro-plan) with Claude Code access. [OpenAI lists ChatGPT Plus at $20](https://help.openai.com/en/articles/6950777-what-is-chatgpt-plus), and its current Codex plan page says Plus includes Codex. The prices can differ by region, tax, billing cycle, and any offer shown in your account. The price column doesn't tell you how many useful tasks each plan will finish. Cursor measures agent use against included capacity, Claude varies usage with conversation length and model use, and Codex consumption changes with task size. Read the account meter during a normal week before comparing plans from headline prices alone. ## Is One AI Coding Tool Enough for Solo Work? One AI coding tool is enough for a solo developer until a repeated task or usage limit justifies another subscription. Start with the tool that fits your normal work, then judge it by completed features, review time, mistakes, and the limits you actually hit. Try it on a bug, a small feature, and a test in a project you understand. Lines of generated code say little about whether the subscription helped. The useful measure is how much working code you can check and ship without adding repair work. ## When Should You Add a Second Coding Agent? A second coding agent earns its bill when it performs a repeated handoff that your main tool does not handle well. It might review a risky patch, inspect a plan before edits, or take a cloud task while the main tool stays interactive. The handoff should be named before the subscription starts. My own pairing uses Claude Code and Codex as co-equal agents on separate jobs. They can also review each other's work when an edit carries enough risk to justify another pass. I don't let both agents invent separate versions of the same task, because disagreement without a shared brief creates noise instead of evidence. The handoff needs a small written packet: the accepted plan, files in scope, check commands, and boundaries. Give the same packet to both tools. One agent owns implementation, and the other reports possible misses without changing the job underneath it. Use another agent for decisions that are costly to reverse. Authentication, payment paths, data moves, and package changes can justify an independent read. A label edit or small CSS fix rarely needs an AI review panel; reading the patch yourself will be quicker. ## Make Agent Work Reviewable Solo work needs smaller agent batches because one person carries the whole review load. Give each task a visible result, a narrow file list, and a command that proves the result. Ask for a plan before edits when the job reaches data, permissions, or several parts of the repository. Project instruction files carry decisions that another teammate might remember. Keep `AGENTS.md`, `CLAUDE.md`, or the editor's repository instructions current with the build command and test command. Add a specific boundary when a correction repeats. I use hooks and instruction files because repeating the same warning inside chat failed too often. The first repeated miss is a signal to encode the rule. A short guard that blocks headless browser launches or an unwanted package does more work than another paragraph asking the agent to be careful. Inspect every deletion before you spend time on additions. Removed validation, error handling, and tests can make a patch look smaller while raising its risk. Run the exact behaviour yourself after the repository checks pass, because a green build can coexist with the wrong user result. Keep recovery cheap enough to use without hesitation. Start from a known state, isolate unrelated edits, and stop when the agent reaches files outside the brief. If you cannot explain the result, undo that change and split the job. A solo developer has no gain from keeping confusing code only because it arrived quickly. ## When Are Cloud Agents Useful for Solo Developers? Cloud agents help solo developers delegate a defined task while keeping their local editor free. I point Cursor cloud agents at auto-deployed test sites, so the task can name both the code edit and the page where the result must appear. Give the cloud agent a starting branch, a clear task, and the checks and preview it should use. Review its returned changes as carefully as local edits. The [Cursor workflows guide](/ai-coding-tools/cursor/workflows/) covers the longer handoff and review process. ## Which Tools Can Solo Developers Skip? Solo developers can usually skip enterprise administration features, overlapping subscriptions, and complex agent swarms until a specific job requires them. Team plans often sell controls aimed at managing several people. Central seat billing, identity login, pooled allowances, policy dashboards, and audit exports solve coordination across an organization. They can become required under a client contract, but they rarely make one developer's code better. Skip a third general coding subscription until the first two have separate jobs you use every week. Model access overlaps across products, and another logo can hide that you have bought another route to similar models. Check which tool produced the last ten shipped changes before adding one more bill. Large local models are also a poor saving plan when they need new hardware. Local execution can make sense for private code, offline work, or model testing. Spending several thousand dollars to avoid a $20 monthly plan requires a long payback period and still leaves setup and power costs. Browser builders such as Lovable or v0 can help a solo founder test an interaction before the production build. Keep that prototype tied to one product question, then rebuild or harden it in the stack you can maintain. Their use here is a researched option rather than a claim about daily firsthand work. Skip automated swarms until one agent is reliable on the repository. Parallel jobs multiply vague assumptions, review queues, and conflicts. Run the manual sequence enough times to learn where a handoff helps, then automate the part with a stable input and an observable result. ## How Much Should Solo Developers Budget for AI Coding Tools? Solo developers should start with a free plan or one individual subscription, then budget separately for extra usage. Start free while you are learning the work pattern. [The free AI coding tools guide](/ai-coding-tools/guides/free-tools/) covers Copilot Free, Antigravity CLI, Cursor Free, limited Codex desktop access, OpenCode, and Ollama. A free trial gives you enough evidence to decide which tool is worth paying for. Current entry options include Cursor Plus at $8, Copilot Pro at $10, and Claude Pro or ChatGPT Plus at $20 per month. Cursor Premium is $30 for its broader agent features. Two subscriptions need a recurring handoff that one product cannot cover well; the total depends on the plans you choose. Use subscriptions for regular work and raw API billing for jobs you understand. API meters can grow during long agent sessions, while the bill lives outside the editor. Set a hard provider cap and turn off automatic reload before connecting a paid credential. Review the bill against shipped work at the end of each month. Count completed tasks, time spent checking generated changes, and repairs caused by bad output. Cancel one plan for a month when its job is unclear. You will soon learn whether you miss the workflow or only the option to open another tool. Official pricing pages move faster than most published articles. Check the official page before paying, and record the plan name shown in your own account. A grandfathered annual plan, local taxes, or a temporary offer can make your checkout differ from a US monthly table. ## Solo Developer Verdict Cursor is my pick for solo developers who want a visual AI editor. Choose Claude Code when repository investigation, debugging, and command-driven work dominate your day; choose Codex when you want to delegate tasks across local and cloud workflows. Copilot fits developers who want assistance without switching their existing editor. Start with one tool that fits your work. My own multi-agent setup grew from recurring jobs, and a second subscription should earn its place the same way. This is a shortlist for solo workflows, while our main ranking weighs the broader strengths of each tool. ## FAQ ### What is the best AI coding tool for a solo developer? Cursor remains our pick for solo developers who want a full AI editor. Premium is the current $30 monthly tier that includes Grok Bot, cloud agents, and automations; Plus is a cheaper, narrower option. Beyond that, choose by the surface you use most: a terminal agent for repository-wide command work, an AI editor for visual diffs, or an editor assistant for inline help. Test one subscription before adding another. ### Is one AI coding tool enough? One paid AI coding tool is enough until you can name a repeated job it handles poorly or a usage limit that blocks shipped work. Starting with one subscription also makes the test easier to read. You can link time saved, review time, and mistakes to one adjustment in the workflow. ### Should solo developers use two coding agents? Solo developers should use two coding agents when the second has a fixed job, such as reviewing a risky diff or checking a plan before execution. Avoid running two open-ended agents on the same task. Give both the same acceptance criteria, keep one owner for implementation, and make the second agent advisory. ### Is Cursor or Claude Code better for solo work? Choose Cursor for a full editor with visual files and diffs; choose Claude Code for a Claude-focused desktop or terminal agent workflow. Claude Code offers desktop and terminal workflows for inspecting a repository, editing files, and running commands. Run the same clearly defined task in your preferred editor, desktop app or terminal, then compare review time and the returned diff. ### How much should a solo developer spend? Solo developers should start with a free AI coding tool or one individual subscription. Current US monthly prices include Cursor Plus at $8, Copilot Pro at $10, Claude Pro and ChatGPT Plus at $20, and Cursor Premium at $30. Add another plan only after a repeated handoff or usage limit justifies the extra bill. Updated in July 2026 with individual plan prices and product access checked against official vendor pages on July 28, 2026. Updated September 23, 2026: corrected Gemini CLI access and switched the free terminal pick to Antigravity CLI after Google moved consumer accounts off Gemini CLI. --- ## Free AI Coding Tools (2026) URL: /ai-coding-tools/guides/free-tools/ Cluster: AI Coding Tools > Guides Copilot Free is our first pick for trying code completion in an existing editor. Cursor Free suits an AI-editor trial, Antigravity CLI offers a quota-limited terminal agent, and ChatGPT Free provides limited Codex desktop access. OpenCode and Ollama are different: the software can be free while model hosting or hardware still costs money. This ranking compares no-subscription starting points, not our overall paid-tool preferences. A useful free plan should let you finish and review a known task before you decide to pay. ## Best Free AI Coding Tools This ranks no-subscription starting points, distinguishing hosted free tiers from free software and local models. It is not our overall tool ranking. In [the main ranking](/ai-coding-tools/), Claude Code is our number-one pick, Cursor is second and Codex third, while Copilot sits well below them; a generous free tier does not change what we recommend paying for. | Tool | Type of Free Access | Main Limit or Cost | Best Fit | | 1. GitHub Copilot Free | Hosted free tier | 2,000 completions monthly; separate limited chat/agent use | Current editor | | 2. Antigravity CLI | Hosted free tier | Basic weekly rate limits | Terminal agent trial | | 3. Cursor Free | Hosted free tier | Limited usage; check account allowance | AI editor trial | | 4. Codex | Hosted free tier | Limited Luna desktop use | Desktop task trial | | 5. OpenCode | Free agent software | Selected hosted models temporarily free; other providers may charge | Provider choice | | 6. Ollama | Local model runner | No per-request local fee; hardware and power costs remain | Local experiments | The ranking gives weight to a useful first hour, clear billing, and a path that doesn't require new hardware. It doesn't rank model intelligence from vendor tests. A free tier that lets you check one known task is more useful than a stronger model available only through a short trial or an unclear meter. ## What Does “Free” Mean for AI Coding Tools? Free AI coding tools offer either a hosted usage allowance, software you can run without a license fee, or local model execution without per-request charges. Those are different promises. A free agent can still call a paid model, and local inference still uses your hardware and electricity. Copilot Free, Cursor Free, Antigravity’s free tier, and ChatGPT Free provide limited hosted access. OpenCode lets you choose a model provider, including selected free hosted models, while Ollama can run models locally. Check which arrangement applies before connecting a paid account. ## Are Any Free AI Coding Tools Unlimited? Local coding models can run without a hosted provider’s request quota, but their speed and capacity depend on your computer. The hosted free tiers in this shortlist have usage limits, and OpenCode’s selected free hosted models are temporary offers. Free software does not make a paid API unlimited or free. ## GitHub Copilot Free for Editors GitHub Copilot Free adds AI help to an editor you may already use. [GitHub's individual-plan page](https://docs.github.com/en/copilot/concepts/billing/individual-plans) lists up to 2,000 monthly code completions, plus limited chat and agent use through automatic model selection. No paid subscription is required for eligible individual accounts. That offer works well as a test because the first task can stay familiar. Install Copilot in VS Code, Visual Studio, or a listed editor, then use it on code you already planned to write. Track which suggestions you accept and how much time you spend correcting them. The limit covers evaluation and lighter personal work. Active daily typing can consume 2,000 suggestions before the month ends, while chat and agent work have their own allowance. Copilot tells you the reset date when you hit the plan limit, so the product itself should settle any dispute with an old article. Copilot Free isn't designed as an enterprise plan. GitHub's documentation says it lacks organization controls such as audit logs, policy management, and file exclusion. A developer testing a personal repository and a company approving confidential code have different jobs. The free plan answers the first one. Verified students should check GitHub's current Copilot Student eligibility page. Student access can include unlimited completions and a larger allowance than Copilot Free, but the account must remain verified and the current terms still apply. ## Antigravity CLI for Terminal Work Antigravity CLI is Google's terminal agent, sharing its agent harness with the Antigravity desktop app. It replaced Gemini CLI as the free consumer option on June 18, 2026, when Google stopped serving Gemini CLI to free, Google AI Pro, and Ultra users. Gemini CLI remains available through enterprise licenses and paid API keys. Antigravity CLI suits developers who already understand shell commands and repository diffs. The free Individual tier costs $0 and provides quota-limited agent access. Editor completion features and terminal-agent model usage are different allowances. [Google's current pricing page](https://antigravity.google/pricing) caps that tier with what it calls basic weekly rate limits and publishes no daily request count. Because the cap resets weekly instead of daily, one heavy session can use a bigger share of the allowance than a Gemini CLI user would have noticed under the old daily counter. Watch the usage indicator during a repository task before assuming a full week of unattended work. ## Cursor Free for an AI Editor Cursor Free is the useful free option when the question is about Cursor itself. The editor combines Tab completion with an agent surface, and its VS Code roots reduce the move for developers who already know that editor family. Your files and many familiar extensions can stay in place during the trial. Cursor's official pricing page promises limited Agent and Chat requests for Free. It doesn't publish a stable numeric ceiling there. The dashboard and in-editor warnings show the allowance tied to your account, which makes them safer than a number copied from an older comparison. Don't turn the unpublished limit into a guess. Cursor has changed its usage model before, and the cost of an agent request depends on the work and model behind it. If the dashboard stops the same useful workflow across several weeks, then you have evidence for a paid-month test. ## Codex on ChatGPT Free Codex works in several apps and interfaces, but ChatGPT Free access is limited to Luna in the desktop app under the current plan. Do not assume the Free plan includes the CLI, editor extension, or cloud tasks. Start with one local task and a clear way to check the result. [OpenAI's current plan article](https://help.openai.com/en/articles/11369540-using-codex-with-your-chatgpt-plan) covers how Codex draws on your ChatGPT plan's allowance. It doesn't promise a fixed message count. Usage varies with task size, how long the agent runs, and how much project context it must hold. I use Codex as a second coding agent and as a reviewer, and scope matters more as the agent gets wider access. A task with named files and an exact check produces work you can assess. An open request produces activity that may have little to do with the result you wanted. ## OpenCode and Ollama [OpenCode](/ai-coding-tools/opencode/) gives you a free agent program with a broad provider list. Its provider documentation says it works with more than 75 model providers, local models, OpenCode plans, and some existing subscriptions. Each connected provider brings its own billing rules and terms. OpenCode also offers selected free hosted models. Its [current model documentation](https://opencode.ai/v2/docs/console/models/) lists Big Pickle, GLM-5.3-Flash Free, and DeepSeek V4.1 Flash Free, among others, as limited-time options. Select the explicitly free variant and check its availability and data terms; the similarly named paid model may still incur charges. A paid API charges separately, an eligible subscription follows its own terms, and a local model uses your computer. Check the selected provider before treating the entire OpenCode setup as free. Ollama's Free plan includes local model use, public models, and light cloud access. Ollama states that models running on your own hardware remain unlimited. Speed still depends on model size, computer memory, and hardware tuning. Local use can keep code on the machine and remove per-request billing. It can also make a small laptop slow, limit which model fits, and turn setup into the main project. Start with a model that fits the hardware you already have. Buying a costly workstation to replace one small subscription puts hardware spending outside the free-tier budget. ## Build a Useful Zero-Dollar Stack A zero-dollar stack works best when each tool has one job. Use Copilot Free for inline suggestions in your current editor. Use Antigravity CLI for a bounded terminal task when you need an agent. Use the limited Codex desktop allowance for a second opinion or one bounded task. That is enough variety to test completion, editor chat, and agent work without connecting a paid API. Add Cursor Free only when you want to test Cursor's editor. Add Ollama only when local execution answers a privacy, offline, or model-testing need. Run the same small task in any tools you compare. Record the time spent setting up, reviewing the diff, and fixing mistakes. The output that looked fastest in a demo may lose once review time enters the count. - Editor job: Copilot Free completes code you already planned. - Terminal job: Antigravity CLI handles one bounded repository task. - Second opinion: Codex reviews a short diff within the free desktop allowance. - Local trial: Ollama runs a small model on existing hardware. Do not connect four tools to the same repository on the first day. Start with one surface and one task, then add the second only when it answers a different question. Free software still costs attention, and setup time is part of the bill. ## Know When Free Costs More A free tier stops paying when the same limit breaks useful work across several weeks. One busy afternoon can still be misleading noise. A repeated reset wait during known tasks shows that the plan is now part of the job, and a paid month can test whether more capacity changes the outcome. Review time also belongs in the final cost. An agent that writes a large patch in ten minutes may leave an hour of checking. A smaller completion tool can produce less code while keeping each accepted edit clear. Count finished, checked work instead of generated lines. API access can erase the saving fastest. OpenCode and other provider-based agents make long model sessions easy to start, while the bill sits in another dashboard. Use a hard account cap, disable automatic reload, and check the meter after the first task. Local models have a different cost curve. Existing hardware and a fitting model can make local use cheap. New high-memory hardware, extra setup, and slower output may cost more than a year of subscriptions. Privacy or offline access can justify that choice, but subscription avoidance alone often cannot. ## Free AI Coding Tools Verdict Copilot Free wins this free-tier ranking on a boring virtue: it publishes its code-completion allowance. Most free tiers make you discover the ceiling by hitting it, and a limit you can plan around beats a bigger allowance you can't verify. That is also the test to carry to any new free tool: if the vendor won't print the number, treat the tier as a demo, not a plan. Use Codex's free desktop allowance when it covers the task, and check the usage meter before relying on it for a longer job. OpenCode is worth trying with a currently free model when you want provider choice; Ollama suits local work on hardware you already own. The [beginner's AI coding guide](/ai-coding-tools/guides/getting-started/) covers the first task, while the [open coding models guide](/ai-coding-tools/comparisons/best-open-source-models/) covers the local path. ## FAQ ### What is the best free AI coding tool? GitHub Copilot Free is our first pick for trying editor assistance at no cost; Cursor Free is an alternative for an AI-first editor, and Antigravity CLI is an option for terminal work. Check the current account allowance before a longer task. ### Is GitHub Copilot Free really free? Eligible individual developers can use Copilot Free without a paid plan. GitHub currently lists up to 2,000 monthly code completions plus limited chat and agent use through automatic model selection. The plan lacks the policy, audit, and management controls that paid organization plans provide. ### Can I use an AI coding agent for free? You can use a coding agent for free within a hosted plan’s allowance or with a suitable local model. Antigravity CLI offers quota-limited terminal work, Cursor Free has limited agent access, and ChatGPT Free provides limited Luna access in the Codex desktop app. Do not assume that Codex’s free desktop allowance includes its CLI, editor extension, or cloud tasks. ### Is OpenCode free to use? OpenCode is free software and offers selected free hosted models, but other model providers may charge. Its free hosted models can be limited-time offers; paid APIs, eligible subscriptions, and local models each have different costs and terms. Check the selected model and provider before starting a task. ### Are local models cheaper than subscriptions? Local coding models can be cheaper than subscriptions when your current computer runs the model well and you accept lower speed or a smaller model. Buying a new high-memory machine only to avoid one monthly plan can take years to repay. Local use makes more sense for privacy, offline work, or model testing. Updated in July 2026 with limits and plan access checked against official vendor pages on July 28, 2026. Updated September 23, 2026: corrected Gemini CLI access and now recommend the free Antigravity CLI for terminal work, since Google moved consumer accounts off Gemini CLI. --- ## How to Use AI Coding Tools: A Beginner’s Guide URL: /ai-coding-tools/guides/getting-started/ Cluster: AI Coding Tools > Guides Start using an AI coding tool with one small task you already know how to check. Choose a desktop app or your current editor, save a recoverable copy of the project, describe the expected result, and review the changes before keeping them. You do not need to learn every model or agent feature first. The useful beginner habit is a short loop: ask, inspect, run, and decide whether the result matches the request. | Starting point | First surface | First job | Stop when | | New to code | Desktop assistant | One visible screen | You can't explain it | | Working developer | Current editor | Known bug or test | Scope spreads | | Terminal user | Coding agent | Bounded repository task | Commands surprise you | Use our [AI coding tools guide](/ai-coding-tools/) to compare the main options, then return here for the first small task and review loop. ## What Do AI Coding Tools Do? An AI coding tool reads the text and code you give it, then predicts a useful response. It may suggest the next line, explain an error, edit several files, or run a command. The output can look certain even when the tool has misunderstood the job, so fluency is never proof that the result is correct. The tool sees only the context available in that session. Context means the prompt, selected files, standing instructions, command output, and any other material the product sends to its model. A long conversation can crowd out an earlier rule. Start a fresh session when the tool forgets a boundary, then place the boundary in the new request. ### Completion, Chat, and Agents Inline completion suggests code while you type. GitHub Copilot and Cursor Tab both work this way. You stay inside the file, and each accepted suggestion is small enough to read at once. This is a gentle first step for someone who already writes code. Chat lets you ask about a file, error, or proposed design. It can explain a stack trace or draft a plan before any files are edited. Chat is useful when you know the question but need help reading the code around it. An agent can inspect many files, make edits, and run commands against their output. Claude Code, Codex, Antigravity CLI, and Cursor Agent all work in this wider mode. The wider reach saves clicks, and it also gives a vague request more places to go wrong. ## Which Tool Should You Use for Your First Task? Complete beginners can start with a desktop coding assistant or a browser builder that shows a running preview. If you already write code, your current editor is a sensible starting point. Choose one route for the exercise below rather than learning several interfaces at once. The [Claude Desktop guide](/ai-coding-tools/claude-code/claude-desktop/) and [Codex Desktop guide](/ai-coding-tools/codex/codex-desktop/) cover app setup. The [vibe coding guide](/vibe-coding/) covers browser builders. For recommendations, use our [AI coding tools comparison](/ai-coding-tools/); for allowances and costs, use the [free AI coding tools guide](/ai-coding-tools/guides/free-tools/). ## Build Your First Page with an AI Coding Tool Your first AI coding task should produce a small result you can understand and test, even if you cannot write the code yourself yet. This exercise creates a local personal page, checks a button, and practices undoing one change. It needs no account system, database, hosting, or paid API. ### 1. Create a Practice Folder Create an empty folder called `my-first-ai-page` and open it in your coding assistant. Give the assistant access to that folder only. If you use a browser builder, start an empty project and use its preview instead of a local browser file. ### 2. Give the Assistant One Task Ask for one HTML file with a result you can recognize. You can adapt this prompt: > Create a single index.html file for a personal page. Include the heading “Hello, I’m Alex” in black text, one short paragraph, and a button labeled “Say hello.” Clicking the button should display “Thanks for visiting!” below it. Keep all styling and JavaScript in this file. Do not install packages, use external services, or publish the page. Explain what you will create before editing, then explain how I can open the finished file. Read the proposed plan. It should involve one file and the three visible elements you requested. Ask the assistant to simplify it if it introduces extra services or setup. ### 3. Open and Test the Page Open `index.html` in your browser, or open the builder’s preview. Confirm that the heading and paragraph appear, click “Say hello,” and check that “Thanks for visiting!” appears below it. Reload and try again. If the button does nothing, report that specific result: “The heading appears, but clicking Say hello does not display the message. Explain the likely cause and fix only that behavior.” Ask the assistant to explain the HTML, styling, and button code in plain language. ### 4. Make One Change and Undo It Once the page works, copy `index.html` to a backup outside the assistant’s working folder, or save a recoverable checkpoint in your builder. Ask the assistant to change only the heading’s text color to blue. Inspect the changed lines and reload the page to check the result. Now stop the assistant and restore the original file from your backup, or use the builder’s documented checkpoint restore. Reload the page and confirm that the original color returns while the button still works. This exercise uses a dedicated project so the restore will not overwrite unrelated work. You have finished when you can identify the requested elements, test the button, explain what changed, and restore the working version. Knowing how to write every line from memory is not a prerequisite. ## Write a Brief You Can Check A useful brief states the observed problem, the wanted result, and the boundaries around the edit. “Fix my page” leaves the agent to decide which failure matters. A better request names the button, the missing message, and the layout that must remain untouched. > The Say hello button does not display its message. Make “Thanks for visiting!” appear below the button, keep the heading and layout unchanged, and explain how I can test the fix. That request gives you four checks: the button responds, the expected message appears, the heading stays unchanged, and the layout stays put. The tool may still miss an edge case, but you can now point to a failed condition instead of arguing about whether the answer looks good. Include examples when wording or format matters. Give the agent one existing button label, test name, or error response that matches the project. Name files that must remain untouched when the task sits near sensitive code. Clear limits reduce the chance of a helpful rewrite spreading across the repository. Ask the agent to state uncertainty before it edits. It should call out missing files, unclear rules, and assumptions that alter the job. This pause is useful for beginners because it turns unstated guesses into questions you can answer or research. ## Review Every Edit Yourself Read the diff before you accept the result. A diff is the view that marks added, removed, and changed lines. Start with deleted code because missing validation and error handling can hide inside a tidy-looking patch. Then ask why every changed file was needed. Run the project and test the exact behaviour yourself. A developer should also run the repository's existing build and test commands. A beginner can click every changed control, refresh the page, and try an empty or wrong input. The agent's success message is a claim until the running result agrees. Watch for changes that raise the risk of the task: - A new package appears without a reason tied to the request. - A command asks for administrator access or wider file access. - Source code or private data is sent to another service. - The tool rewrites unrelated files while fixing one behaviour. - Tests disappear because they expose a failing result. Ask for a file-by-file explanation when you can't follow the patch. Look up every unfamiliar command before running it. If the explanation still doesn't make sense, revert the work and split the task. Keeping code you cannot explain turns today's quick win into tomorrow's slow bug. ## Set Rules, Data, and Cost Limits Decide what the tool may receive before you paste anything. Written school policies may limit how you use generated work. Employers may bar source code, customer records, or secrets from consumer AI accounts. Remove credentials from examples, and ask the person who owns the policy when the boundary isn't written down. Check your account’s usage allowance before a long session. The [free-tools guide](/ai-coding-tools/guides/free-tools/) explains which options have hosted quotas and which need a separate model provider. Set a spending cap before connecting any paid API account. Subscription access and API billing are separate for many products, including Claude and ChatGPT. Check the usage screen midway through the first week, and turn off extra billing until you understand what one normal task consumes. Keep a short session record while you learn. Write down the task, the tool, the files changed, the check you ran, and anything you had to repair. After several sessions, that record shows whether the tool is saving checked work or only producing more drafts. It also gives you a clean starting brief when you return to the same project later. ### Add Project Instructions as Your Work Grows You do not need instruction files or hooks for the first-page exercise. Add them later when you have recurring project rules and understand the checks they control. Short project instruction files prevent the same corrections from returning. Codex reads `AGENTS.md`, Claude Code reads `CLAUDE.md`, and GitHub Copilot can read repository instructions under `.github`. Record the build command, test command, style rules, and forbidden files that matter to this project. I learned to encode a correction when it repeats, rather than keep adding reminders inside chat. Short instruction files now carry the guidance across my agent work, while hooks enforce specific checks. A rule that blocks one unwanted package or one out-of-scope folder edit earns its place; a page of vague advice does not. ## Beginner Verdict Start with the smallest surface that matches what you know today. A complete beginner should use a desktop assistant or browser builder for one visible result. A working developer can try Cursor Free for a visual AI editor, or Copilot Free to keep an existing editor. A terminal agent belongs after you can read its commands and inspect a repository-wide diff. Keep the first task familiar, write the expected result before the session, and check the running work yourself. When that loop feels normal, the [free AI coding tools guide](/ai-coding-tools/guides/free-tools/) shows which no-cost plan to try next, while the [AI coding tool comparisons](/ai-coding-tools/comparisons/) help after you have two serious candidates. ## FAQ ### Which tool should a complete beginner start with? Complete beginners should start with Claude Desktop’s Code tab if they have a paid Claude plan and want a visual first coding session. Beginners who want to try AI coding without paying can start with the limited free access to Codex in the ChatGPT desktop app. If your goal is a browser app built through description, a browser builder can provide a less technical starting surface. ### Do I need to pay to try AI coding? Beginners can try AI coding without paying. GitHub Copilot Free includes up to 2,000 code completions per month, Cursor has a limited Free plan, Antigravity CLI has a free individual tier with weekly rate limits, and Codex offers limited Luna access in the desktop app on ChatGPT Free. Check each official usage page before relying on a fixed allowance. ### What should I build first? A beginner’s first AI coding project should be a simple personal page or tip calculator, because its visible result is easy to describe and check. Keep the exercise small enough to test in one sitting, even if you cannot write all the code yourself. A visible, testable result lets you judge the tool instead of trusting its confidence. ### How do I review AI code I don't understand? You can review unfamiliar AI-generated code by comparing the changed files, asking for an explanation, and testing the intended behavior; ask an experienced developer to check changes you still cannot assess. Look up unfamiliar commands before approving them. Reject new packages, permissions, or services unless you can explain why the task needs them. If the code still makes no sense, revert it and reduce the task. ### How do I recover when an agent changes too much? You can recover from excessive AI coding agent changes by stopping the agent and restoring the affected files to a known-good version using an available undo action, checkpoint, or Git history. Review the changes first and preserve unrelated work so the restore does not remove edits you want to keep. If no recovery point exists, save a copy of the current files and repair the unwanted changes manually or with experienced help. Restart the agent with a smaller task once the project works again. ### Can I use these tools at school or work? You can use AI coding tools at school or work only when the relevant policy permits your intended use and the data you plan to share. The written rules differ between courses and employers. Ask the instructor or security owner in writing when the policy is unclear, then keep a short record of the tool and the work it performed. Updated in July 2026 with product access and free-plan facts checked against official vendor pages on July 28, 2026. Updated September 23, 2026: corrected Gemini CLI access and named Antigravity CLI as the free terminal option, because Google moved consumer accounts off Gemini CLI. --- ## Advanced OpenCode Agents and Automation URL: /ai-coding-tools/opencode/advanced/ Cluster: AI Coding Tools > OpenCode OpenCode's advanced setup controls who acts, which files and commands they can touch, and which external systems they can reach. Agents, permissions, MCP servers, commands, skills, and formatters all change the same thing: the work an AI session may attempt inside a repository. Start with the base files, then add one boundary at a time. The often-misread `doom_loop` permission gets its own section below. These examples use OpenCode 1 configuration. OpenCode 2 can translate supported legacy fields, but its native permission syntax and defaults differ. Version scope: The commands, configuration examples, and permission defaults below describe OpenCode 1. OpenCode 2 supports much existing configuration, but installation, server workflows, and some defaults differ. Read the [V2 migration guide](https://opencode.ai/v2/docs/migrate-v1/) before converting a setup. Both versions use the same `opencode` command, so their default installations can replace one another. If you have not settled on OpenCode, compare the broader [AI coding tools](/ai-coding-tools/) lineup before building a custom agent setup. ## Build the Basic Setup First Advanced configuration will not rescue a weak base setup. Open the repository, run OpenCode, use `/connect` for a provider, and choose the model with `/models`. Run `/init` only when you want OpenCode to draft an `AGENTS.md` file for review. The two main files have different jobs. `AGENTS.md` tells the agent what the project is, how it builds and tests, and which conventions it must follow. `opencode.json` sets providers, models, agents, tools, permissions, MCP servers, formatters, and other program behavior. Try one read-only task before editing either file further. Ask OpenCode to locate a known function, name its callers, and cite every file it reads. Then use Plan for a small failing test. If those steps fail, fix the provider, model, or project context before adding more agents. ## Use the Built-In Agents OpenCode has two built-in primary agents. Build is the default and has all tools enabled. Plan is restricted: file edits and shell commands ask for approval. Switch between primary agents with Tab or the configured agent key. Three built-in subagents handle narrower work. General can research and carry out multi-step tasks with broad tool access. Explore is a fast read-only code searcher. Scout is read-only and focuses on external docs and dependency source. A primary agent can call them, or you can name one with an `@` mention. | Agent | Default Access | Use | | Build | All tools | Scoped edits | | Plan | Edits ask | Plan and review | | Explore | Read-only | Code search | | Scout | Read-only | Upstream research | Use the built-ins until a repeated job needs a different authority boundary. A named "senior engineer" agent with the same tools as Build only adds prompt text. A reviewer with deliberately restricted tools earns its own definition because its authority differs from an implementation agent. ## Which Actions Does OpenCode Allow by Default? OpenCode 1 starts from permissive defaults when no permission config exists. Most permissions are `allow`. The `doom_loop` and `external_directory` guards default to `ask`. Reads default to `allow`, while `.env` files are denied by default. Do not assume Plan makes the whole application read-only. Its edits and shell commands ask for approval, and the response still depends on your choice. Auto-approve can also alter the session's behavior. Check the active mode and config rather than relying on the agent name. Permission actions are `allow`, `ask`, and `deny`. OpenCode applies them to tools such as read, edit, glob, grep, bash, task, external directories, web access, language-server calls, skills, questions, and `doom_loop`. ## Write Ordered Permission Rules Start from the task. Give a reviewer read and search access. A test writer may edit test paths and run one test command, while production code remains outside its authority. A docs agent can work without deployment or package-manager commands. Pattern rules are evaluated in order, and the last matching rule wins. Put the broad wildcard first and the narrow exception after it. The order is part of the security boundary, so read the entire object during review. ``` {`{ "$schema": "https://opencode.ai/config.json", "agent": { "review": { "mode": "subagent", "permission": { "edit": "deny", "webfetch": "deny", "bash": { "*": "ask", "git status *": "allow", "git diff *": "allow", "git log *": "allow" } } } } }`} ``` The wildcard makes unknown shell commands ask. The later Git rules allow three read-only command families. Do not write `"git *": "allow"` for a reviewer, because that also matches commands that change branches, history, remotes, and the working tree. An approval prompt is useful only when you inspect it. Check the parsed command, arguments, working directory, and affected path. A command named "test" may call a script that resets a database or rewrites snapshots. ## Create a Bounded Custom Agent Define a custom agent in `opencode.json` or a Markdown file. Project files live under `.opencode/agents/`; user-wide files live under `~/.config/opencode/agents/`. A repository agent can be reviewed, changed, and kept in step with the code it governs. Give the agent a concrete description so OpenCode knows when to call it. Set its mode to `primary`, `subagent`, or `all`. Add a model only when the job needs a different one. Keep the prompt short and put shared project facts in `AGENTS.md`. ``` {`--- description: Review code with direct edits denied mode: subagent permission: edit: deny webfetch: deny bash: "*": ask "git diff *": allow "git log *": allow --- Report correctness bugs with file and line evidence. Do not edit files.`} ``` The example denies direct file-edit tools, but it is not an enforced read-only environment because approved shell commands can still write files. Deny shell execution too when that is the required boundary. Test both allowed and forbidden actions. Ask the review agent to edit a file and confirm that OpenCode denies it. Ask it to run an unlisted command and confirm that the prompt appears. A config that looks right but permits the forbidden action has failed its main job. ## Understand the `doom_loop` Permission In OpenCode 1, `doom_loop` is a permission guard triggered by the same tool call repeated three times with identical input. It defaults to `ask`. It asks whether the repeated action may continue; deny it when another attempt has no new evidence. The permission controls continuation of the repeated action. Iteration caps, fresh-context retries, and proof of a different result require separate controls. External scripts that run OpenCode again are a separate automation pattern with their own limits, spend controls, clean state, and validation. When the guard fires, inspect what repeated. The same failed command may mean the tool is missing. The same edit may mean the test and requirement conflict. Repeated reads can mean the task is too broad or the model has lost the goal. - Stop after the same failure appears twice without new evidence. - Read the current diff before approving a recovery attempt. - Narrow the task or change the model when tool use is the problem. - Escalate product and security decisions to a person. ## Connect One MCP Server MCP servers add tools from another service. OpenCode supports local servers started by a command and remote servers reached by URL. Remote servers can use headers or OAuth, and OpenCode provides commands to authenticate, list, debug, and log out of them. Add one server that removes a current manual step. A read-only error tracker can expose open issues. A documentation server can answer API questions. Give it a narrow credential, keep secrets in environment references, and find out whether any tool can write before enabling it. ``` {`{ "$schema": "https://opencode.ai/config.json", "mcp": { "docs": { "type": "remote", "url": "https://docs.example.com/mcp", "enabled": true } }, "permission": { "docs_*": "ask" } }`} ``` OpenCode warns that MCP tool definitions use model context. A large catalog can exceed the context limit before the agent reads much code. Disable servers you do not need, or hide their tools globally and allow them only on the agent with that job. Use `opencode mcp list` to inspect status. For an OAuth server, use `opencode mcp auth SERVER_NAME` and `opencode mcp logout SERVER_NAME`. Do not commit a bearer token into `opencode.json`. ## Add Commands, Skills, and Formatters Custom commands store a repeated prompt and expose it through a slash command. Use one for a job with a stable input and output, such as reviewing the current diff against repository rules. A command can name an agent, model, file reference, or shell output. ### Load a Skill for a Repeated Workflow Agent skills are folders with a `SKILL.md` file. OpenCode discovers project skills under `.opencode/skills/` and compatible skill folders under `.claude/` and `.agents/`. Skills load on demand, and their access can be allowed, denied, or set to ask by name pattern. ### Enable Formatters Deliberately Formatters are disabled when the formatter setting is omitted. Enable built-ins with `"formatter": true`, or configure one by file extension and command. A formatter runs after OpenCode writes or edits a matching file, so enabling one can enlarge the diff. | Feature | Stores | Main Risk | | Command | Repeated prompt | Stale task steps | | Skill | Reusable workflow | Broad tool access | | Formatter | Write hook | Large diffs | | MCP server | External tools | Data and context | ## Review Configuration as Code Review authority changes in every OpenCode config diff. A new wildcard, external directory, MCP server, skill path, formatter, or subagent can alter later sessions even when the current code diff is small. State that authority change in the pull request. - Read the final merged permission order. - Test one allowed action and one denied action. - Check every new credential source and remote URL. - Run the repository checks after formatters or tools change files. - Remove agents, commands, and servers that no longer have an owner. Then inspect the work product as if it came from an unsupervised contributor. Confirm the changed-file list, read the diff, run the checks outside the agent's own summary, and reject unexplained changes even when the final build passes. The [OpenCode local models guide](/ai-coding-tools/opencode/local-models/) covers Ollama, context sizing, and the network boundary. The [OpenCode overview](/ai-coding-tools/opencode/) covers the basic fit, provider choice, and first task. ## Boundaries Worth Keeping OpenCode's advanced features pay off when each one creates a boundary you can name and test. Plan before Build, deny edits for reviewers, order wildcard rules carefully, and connect one MCP server only when its credential, tools, and context cost are understood. Treat `doom_loop` as a approval to continue repeated tool calls. Keep agents, commands, skills, formatters, and MCP servers in normal code review, and remove any configuration whose owner or purpose is no longer clear. ## FAQ ### What does doom_loop do in OpenCode? In OpenCode 1, `doom_loop` asks about a repeated action after three identical calls to the same tool. It defaults to `ask`. It authorizes continuation of repeated tool calls after a repeated call; it provides no retry count or autonomous overnight loop. Review the repeated call before approving recovery. ### Which OpenCode agent should plan a change? OpenCode’s Plan agent is the appropriate choice for analyzing a change before editing files. OpenCode documents Plan as a restricted primary agent whose edits and shell commands ask for approval. Build is the default primary agent with all tools enabled. Explore and Scout are read-only subagents for codebase and external-source research. ### Where do custom OpenCode agents live? Custom OpenCode agents are defined in `opencode.json` or as Markdown files under `.opencode/agents/` for a project and `~/.config/opencode/agents/` for a user. Give each agent a description, mode, prompt, model only when needed, and explicit permissions. Repository agents can be reviewed with the code. ### Do MCP servers use model context? MCP tool definitions consume model context in OpenCode; a large tool catalog can exceed the context limit. Keep only the servers needed for the task enabled. Disable unused tools globally or expose a server only to the agent that needs it. --- ## OpenCode for Regulated and Enterprise Teams URL: /ai-coding-tools/opencode/enterprise/ Cluster: AI Coding Tools > OpenCode You can deploy the free OpenCode client on company-controlled machines and connect it to an approved model endpoint. OpenCode Enterprise is a separate commercial offering for organization-wide configuration, identity integration, and a vendor relationship. Buying Enterprise is not required simply to use your own model gateway. For me, the attraction is control over where requests go and how the agent runs. That control brings operating work. Someone must maintain the configuration, credentials, updates, and evidence that the chosen deployment meets the team's requirements. Version scope: The commands, configuration examples, and permission defaults below describe OpenCode 1. OpenCode 2 supports much existing configuration, but installation, server workflows, and some defaults differ. Read the [V2 migration guide](https://opencode.ai/v2/docs/migrate-v1/) before converting a setup. Both versions use the same `opencode` command, so their default installations can replace one another. For an initial product shortlist, our [guide to AI coding tools](/ai-coding-tools/) covers OpenCode and the proprietary alternatives. This guide focuses on evaluating the OpenCode deployment path. ## Can OpenCode Meet Your Data-Handling Requirements? OpenCode can support a controlled deployment, but the answer depends on the model endpoint, tools, storage, and network controls you configure. A locally installed agent can still send code to a hosted model or remote MCP server. Evaluate that complete path before deciding whether the free client or Enterprise service fits. ## Define Where Code Is Allowed to Go Most tool evaluations start with a feature grid and arrive at the security questions late. Reverse it. Write down the one requirement that would disqualify a tool outright, and test that first. - Code must never leave our network: use inference on a local machine or an internal model server, with other outbound paths controlled. - Code may leave, but must not be retained: provider retention terms decide it. - We need audit evidence for a regulator: request evidence for the actual services and controls in your deployment. - We need it approved this quarter: published compliance documents win on speed. OpenCode gives you configuration options for the first two requirements. On the third it is weaker: its [public Enterprise page](https://opencode.ai/docs/enterprise/) states no certification and links no audit report, so that evidence has to be requested under the Enterprise agreement rather than assumed from a provider's paperwork. ## Map Every Outbound Path OpenCode does not store code or context on its own service by default. Session data stays on your machine, and `/share` sends a shared conversation to OpenCode's hosting service. A single session can reach the network through several independent paths, and each one has its own destination, credentials, and logs. | Path | Goes To | Control | | Model requests | Provider or localhost | Provider config | | Remote MCP servers | Third-party endpoints | Disable per server | | Package installs | Public registries | Permission rules | | Git operations | Your remotes | Deny rules on push | Run that list before a pilot, not after. A team that verified the model path and forgot the MCP path has verified a quarter of its perimeter and reported it as the whole thing. The local-inference half of this has its own walkthrough in [OpenCode local models with Ollama](/ai-coding-tools/opencode/local-models/), including where the offline claim actually holds. ## Run OpenCode Inside Your Own Infrastructure The deployment story is the strongest part of the case. OpenCode runs on machines you already own, against a gateway you already control, with no vendor tenancy in the middle. That gives a security team three things a hosted product usually cannot: 1. The agent's source is readable, so behaviour can be verified rather than trusted. 2. The model endpoint is yours, so retention is governed by your own contract. 3. Nothing is stored on OpenCode's service by default, so there is no vendor-side copy to reason about. The cost is that operating it becomes your job. Upgrades, availability, and the gateway's own monitoring all land on an internal team, and a fast-moving open-source release cadence means those upgrades arrive often. ## What OpenCode Enterprise Adds The open-source tool is not the only option any more, and evaluations written a few months ago miss this. OpenCode sells a per-seat Enterprise product, with a sales and implementation contact rather than a community forum as the only route in. No seat price is published, so the figure has to come from a sales quote. | Enterprise adds | What It Covers | | Central config | Organization-wide defaults | | SSO | Gateway credentials via your identity provider | | Internal gateway | Only yours; other providers disabled | | Pricing | Per seat, unpublished; quote from sales | | Vendor contact | Sales and implementation | That changes the shape of the decision. Organization-wide enforcement and identity are now a product you can buy rather than something you assemble, and bringing your own gateway means OpenCode adds no token charge on top of what your provider bills. ### Internal Gateway Versus Bringing Your Own The internal-gateway row is the one most likely to be misread, so read it slowly. Pointing OpenCode at a gateway you run is already free: a provider entry in `opencode.json` sets the base URL and the open-source tool sends its requests there. Buying Enterprise is not how you get a self-hosted endpoint. That entry is the whole mechanism, in the shape OpenCode's provider documentation gives for an OpenAI-compatible `/v1/chat/completions` endpoint. A gateway serving `/v1/responses` instead takes the `@ai-sdk/openai` package rather than the one below. Substitute your own gateway URL and the model IDs it serves: ``` {`{ "$schema": "https://opencode.ai/config.json", "provider": { "gateway": { "npm": "@ai-sdk/openai-compatible", "name": "Internal gateway", "options": { "baseURL": "https://llm.internal.example.com/v1" }, "models": { "your-gateway-model-id": { "name": "Internal gateway model" } } } } }`} ``` No OpenCode Enterprise agreement is needed for that. Commit the file and every clone receives the same gateway configuration, which is exactly why the Enterprise value has to be something other than the endpoint itself. What the Enterprise central config adds is stated in three parts on the same page. It is set up once for the whole organization rather than repository by repository. It can integrate with your SSO provider so OpenCode obtains the gateway credential through your existing identity system. The third part is the one procurement cares about: the central config can be set to use only your internal gateway, with all other AI providers disabled. That is the centrally managed restriction OpenCode Enterprise offers. One thing the public docs do not state is which configuration layer carries that central config, and the answer decides how strong the restriction is. OpenCode publishes an eight-source precedence order in which remote organizational config from `.well-known/opencode` loads first as a base layer that global and project files can override. Managed config files and macOS MDM preferences sit at the top of that same order and are described as not user-overridable. The Enterprise page does not say which layer it uses, so ask before you write "other providers are disabled" into a control description. What none of this changes is the evidence question set out in [Define Where Code Is Allowed to Go](#start-from-the-perimeter-not-the-brand). Enterprise-specific liability and support terms are not published on the Enterprise page, so ask how the public Terms of Use relate to the Enterprise agreement. ## Choose Between Shared Defaults and Enforcement Access control for a local agent is mostly your existing controls: who can log into the machine, who holds the provider credential, and who can push to the repository. The agent inherits all three rather than replacing them. Shared configuration is where a team gets consistency. A project-root `opencode.json` is documented as safe to commit and takes highest standard precedence, so every clone starts from the same providers and permission rules. OpenCode 2 makes this distinction explicit: ordinary settings do not prevent users from adding providers or tools; enforced restrictions belong in policies at an appropriate managed or system layer. Be precise about what that buys you. A checked-in file gives the team shared defaults that everyone can inspect, and it can still be changed or overridden locally, so a repository config is not enforcement. Organization-wide enforcement comes from Enterprise central config or from admin-controlled managed settings. Credential handling deserves one explicit rule. Provider keys belong in the environment or a secrets manager, never in a configuration file that gets committed, because the whole point of the shared file is that it is readable by everyone. ## Permissions Are Your Policy Surface OpenCode ships with permissive settings. Out of the box it will run most operations without asking, and you tighten it yourself. For an individual developer that is a preference. For a regulated team it is the single most important configuration step. Write the rules before the pilot, not after the first incident. A deny rule on pushes, an ask rule on anything that installs software, and an allowlist for the commands your build actually needs covers most of what a review board asks about. The rule syntax, ordering, and the bounded-loop guards are covered in the [advanced OpenCode guide](/ai-coding-tools/opencode/advanced/). Treat that file as the written policy, and keep it in review like any other control. ## What Procurement Will Ask For A vendor relationship now exists, so the old "there is nobody to ask" answer is wrong. What is still missing is the Enterprise-specific document set, unchanged from [the perimeter check](#start-from-the-perimeter-not-the-brand). The Status column below is the at-a-glance version. Published means the answer is in the public docs, request means ask the vendor for it during contracting, and self-managed means it never was theirs to answer. | Question | Status | Where the Answer Comes From | | Audit report | Request | Not linked; ask Enterprise sales | | Data residency | Depends on the whole deployment | Your own infrastructure and model provider | | Access control | Published | SSO via Enterprise central config | | Enterprise-specific terms | Request | Request during Enterprise contracting | | Support commitment | Request | Not stated; ask Enterprise sales | Disabling sharing closes one data path, not every path. The model gateway, its upstream providers, MCP servers, logs, Git remotes, and package tools all need locations and retention policies that match your requirements. An internal gateway can still forward requests outside your network. A hosting or model provider's attestation covers that provider's service, not OpenCode's central config, SSO, supply chain, or contract. Do not let one stand in for the other in a submission. Every row that stays yours is real work with a real owner, so name that owner in the proposal rather than leaving the cell to imply somebody has it. ## Where a Commercial Vendor Wins A commercial editor offers published compliance evidence, single sign-on, directory provisioning, and audit logs, most of them admin-configured rather than on out of the box. For a team whose blocker is a procurement calendar rather than a network boundary, that is still the faster path. The trade runs the other way when the requirement is absolute. If code cannot leave the perimeter, a hosted agent that routes requests through a vendor backend cannot satisfy it, however good the paperwork is. The full head-to-head on that trade, including pricing and the agent layer, is in [Cursor vs OpenCode](/ai-coding-tools/comparisons/cursor-vs-opencode/). ## Decide on the Requirement You Can Prove Pick the smallest deployment that satisfies your hardest requirement. For most teams that is OpenCode against an internal gateway with checked-in permission rules, not a full air-gapped build that nobody has the hardware to run well. Add Enterprise when the requirement is organization-wide enforcement, SSO, or a vendor contract, and price that per seat against what assembling the same controls would cost you. Start from the [OpenCode overview](/ai-coding-tools/opencode/) for the basics, and use the [advanced guide](/ai-coding-tools/opencode/advanced/) to write the permission rules your pilot will be judged on. ## FAQ Vendor facts on this page were checked against OpenCode's own documentation on August 9, 2026. The Enterprise product details, the internal-gateway distinction, and the config precedence order were re-verified on August 10, 2026. ### Is OpenCode SOC 2 certified? OpenCode’s own SOC 2 certification is unverified in the public Enterprise material reviewed for this guide; request a current report rather than assuming certification. Your hosting and model providers have their own attestations, and those cover their services only. They say nothing about OpenCode's central config, SSO, supply chain, or contractual controls. ### Does OpenCode store our code? OpenCode does not store code or context on its service by default. Session data stays local, and the share command sends that conversation to OpenCode's hosting service. Your model provider, MCP servers, package registries, and Git remotes each keep their own retention terms. ### Can OpenCode run fully air-gapped? OpenCode can run fully air-gapped only when the model, tools, and supporting services operate without external network access. Say local model inference when that is what you verified, and reserve air-gapped for a setup where the provider, tools, package installs, Git remotes, and telemetry have all been checked against the claim. ### How do we enforce one configuration across a team? Teams can enforce a shared OpenCode configuration using managed or MDM settings; checked-in project configuration provides shared defaults that developers may still override. OpenCode Enterprise sells one organization-wide enforcement path through its central config, including the option to allow only your internal gateway. Ask which config layer it ships through, because OpenCode's published precedence order makes remote organizational defaults overridable while managed and MDM settings are not. ### Who owns the model bill? Your organization pays the model provider or gateway account used by its OpenCode configuration; local inference instead uses your own hardware and operating budget. OpenCode Enterprise is priced per seat, with no published figure, and adds no token charge when you bring your own LLM gateway. Budget the model spend per team, because usage varies more than headcount does. ### What should a pilot actually measure? An OpenCode pilot should measure where requests went, what commands ran, and how much review each change needed. Speed claims are the easiest to gather and the least useful for a compliance decision; the data path and the command log are what a security review will ask about. --- ## OpenCode Local Models with Ollama URL: /ai-coding-tools/opencode/local-models/ Cluster: AI Coding Tools > OpenCode [OpenCode](/ai-coding-tools/opencode/) can send its model requests to Ollama on your own computer. That gives the terminal agent a local inference path while OpenCode still handles files, commands, permissions, and the session. Adding the provider takes minutes. Model choice and context size decide whether it can use tools on your code. Connect Ollama, test it on a repository, and verify the wider network path before calling the workflow offline. Kimi K3 and GLM-5.2 belong in the datacenter hardware class rather than a laptop download list. Model and provider facts were checked on July 28, 2026; the model sizes, tags, and hardware notes in the table below were verified against Ollama's library on August 10, 2026. Version scope: The commands, configuration examples, and permission defaults below describe OpenCode 1. OpenCode 2 supports much existing configuration, but installation, server workflows, and some defaults differ. Read the [V2 migration guide](https://opencode.ai/v2/docs/migrate-v1/) before converting a setup. Both versions use the same `opencode` command, so their default installations can replace one another. Local inference is one way to run a coding assistant. Our [AI coding tools overview](/ai-coding-tools/) compares that approach with hosted and editor-based options. Use the [open-model shortlist](/ai-coding-tools/comparisons/best-open-source-models/) to narrow your candidates, then check [OpenCode’s enterprise data paths](/ai-coding-tools/opencode/enterprise/) if local inference is part of an organizational requirement. ## How Do OpenCode and Ollama Work Together? OpenCode manages the coding session and tools; Ollama runs the selected local model and returns its responses. With a hosted provider, OpenCode sends the prompt and selected code context to that provider for inference. With Ollama, OpenCode calls an API on `localhost`, and the model runs on the machine serving that API. Ollama documents the local endpoint as unauthenticated because it is local to the host. The model may still need gigabytes of memory and enough context for the prompt, files, tool definitions, and tool results. A small model that answers coding questions can fail as an agent because it emits the wrong tool call, forgets an instruction, or runs out of context during a multi-file task. Cost moves from a per-token invoice to hardware, power, memory, and time. The OpenCode application remains free to install, while the machine doing inference becomes part of the bill. Compare total task time and operating cost alongside the missing API charge. ## Install Ollama and Pull a Model Install Ollama from its [official documentation](https://docs.ollama.com/). Use the installer for your operating system rather than copying an old package command. After the app or service starts, pull a model from the Ollama library: ``` ollama pull devstral:24b ``` That is a real tag on the [Ollama model library](https://ollama.com/library), and the same command works with any other exact ID shown on a model page. Run it once in Ollama before involving OpenCode. Ask a short coding question, then use `ollama ps` to see the loaded model, processor split, and allocated context. Prefer a model whose Ollama page or maker documents tool use. Agent work needs reliable function calls as well as text generation. Start smaller than the largest model that can fit, because a response that takes several minutes makes every plan, search, edit, and retry expensive in wall-clock time. ## Open Models That Run Well Locally I would evaluate the agent and model together rather than assuming the biggest model is the best choice. Several open coding models are built for the work a local agent actually does, and they matter more than the headline flagship names because they fit on hardware you already have. | Model | Built For | Size (4-Bit) | | [Devstral 24B](https://ollama.com/library/devstral) | Agentic coding tasks | 14 GB | | [Qwen3-Coder 30B](https://ollama.com/library/qwen3-coder) | General coding work | 19 GB | | [Qwen2.5-Coder 32B](https://ollama.com/library/qwen2.5-coder) | General coding, smaller footprint | 20 GB | | [Codestral 22B](https://ollama.com/library/codestral) | Fill-in-the-middle autocomplete | 13 GB | Those sizes are the download, not the whole requirement. Ollama's Devstral page calls the 24B model light enough for a single RTX 4090 or a Mac with 32 GB of RAM, comfortably above its 14 GB file. Parameter count alone does not tell you what fits. A 4-bit 24B model can run on some 24 GB GPUs at a moderate context, while the same model at 8-bit roughly doubles that footprint. Quantization, context length, runtime overhead, and whether the memory is dedicated GPU or unified system memory all decide whether it runs comfortably. Judge by your repository test, not by the parameter count. My preference is to judge the complete agent setup. Reliable tool calls and a working local connection can matter more than a model's headline ranking, but the model, context, and hardware can each become the limiting factor. ## Connect OpenCode to Ollama For OpenCode 1, the provider guide includes an Ollama path and the manual OpenAI-compatible configuration below. OpenCode 2 can discover Ollama at `127.0.0.1:11434` automatically; start Ollama, pull a supported model, and choose it with `/models`. The local service listens at `http://localhost:11434`, while the compatibility endpoint adds `/v1`. Use the exact model ID you pulled so it appears in OpenCode's model picker. ``` {`{ "$schema": "https://opencode.ai/config.json", "provider": { "ollama": { "npm": "@ai-sdk/openai-compatible", "name": "Ollama", "options": { "baseURL": "http://localhost:11434/v1" }, "models": { "devstral:24b": { "name": "Local coding model" } } } } }`} ``` The `npm` field names the provider adapter used by OpenCode. You do not run a package-manager command for this step. Save it as `opencode.json` in the project root, which OpenCode documents as safe to commit and gives highest standard precedence. Then open OpenCode in the repository, run `/models`, and choose the local model. Test the path with a read-only prompt: "Name the primary language, find the test directory, and cite the files you read. Do not edit or run commands." If the answer names the wrong code, fix the model or context setup before granting edit access. ## How Much Context Does a Coding Agent Need? Ollama recommends at least 64K tokens for coding agents, provided the model and available memory support it. Context length is the number of tokens the model can keep in one request. Ollama's current defaults vary with graphics memory: 4K below 24 GiB, 32K from 24 to 48 GiB, and 256K at 48 GiB or more. Those defaults were checked on July 28, 2026. OpenCode's provider page says to raise `num_ctx` when tool calls fail, starting around 16K to 32K. Ollama's context guide says coding tools and agents should use at least 64K. The second figure is a workload recommendation, and available memory still decides whether the computer can hold it. On a system with enough memory, you can start the Ollama service with a larger context: ``` OLLAMA_CONTEXT_LENGTH=64000 ollama serve ``` A larger window uses more memory. Run `ollama ps` and check the allocated context and processor split. If much of the model moves to the CPU, a smaller model or context may finish the full task sooner. | Failure | Likely Cause | Next Check | | Tool call rejected | Model or context | Raise context | | Very slow turn | CPU offload | `ollama ps` | | Forgets files | Window too small | Narrow task | | Wrong tool syntax | Model mismatch | Try another model | ## Choose a Model with a Repository Test Do not pick from a general benchmark alone. Use one small task from the repository with an objective end: explain a failing unit test, propose the smallest fix, edit one implementation file, and run the focused test. Keep the prompt, permissions, and context the same for every model. - Record time to the first useful plan. - Count incorrect file reads and failed tool calls. - Inspect the diff rather than grading its explanation. - Run the test yourself after the model stops. - Note peak memory and whether Ollama offloaded work to the CPU. Then try a second task that needs repository context, such as changing an API field and its tests. A model that passes the first isolated fix may lose track of a change across several files. Stop when retries exceed the time saved by local inference. Use permissions during the test. Begin with reads allowed and edits or shell commands set to ask. Local weights leave OpenCode's file and command authority unchanged, so review the model's commands with the same care as a hosted model's. ## Place Kimi K3 and GLM-5.2 Correctly Kimi K3 and GLM-5.2 belong in the flagship server class; neither is a normal laptop choice. Moonshot's July 16 Kimi K3 page names 2.8 trillion total parameters, 16 active experts out of 896, and a context window of one million tokens. It recommends a supernode with 64 or more accelerators for deployment. Kimi K3 weights are now published in the [official model repository](https://huggingface.co/moonshotai/Kimi-K3) under the Kimi K3 License. Published weights do not make a datacenter-scale model a practical laptop download. Z.ai's official GLM-5.2 model card is live on Hugging Face with an MIT license. It lists 753 billion parameters and a one-million-token context, plus serving paths for vLLM, SGLang, Docker, and quantized variants. Those options make the model self-hostable infrastructure, while performance on one workstation still needs its own test. For local OpenCode work, choose a smaller model built for tool use. Use Kimi K3 or GLM-5.2 through a hosted provider unless you operate the accelerator capacity and serving stack their full checkpoints require. ## Use Local and Hosted Models Together OpenCode can keep several providers in one model list. A local model can handle code search, explanation, small tests, and repetitive edits. A hosted model can take the task when it needs a larger context, stronger tool use, or better reasoning across many files. Keep the acceptance test unchanged when you switch. Otherwise you cannot tell whether the model solved the task or the task became easier. Check the active provider before a long run so private code does not move to a hosted API by accident. A hybrid setup also gives you a fallback when the local service is slow or a hosted provider is down. It does not require every task to bounce between models. Pick one model for a run, finish or stop the run, then restart with the other model and the same state. ## Check the Whole Privacy Boundary Local Ollama inference keeps that model request on the local endpoint. OpenCode can still fetch a URL, call a remote MCP server, push to Git, install a package, or switch to a hosted provider. Each action has its own destination, credentials, and logs. For a repository with strict data rules, that makes the boundary a checklist rather than a setting. Work through it before the first run: - List every enabled provider and tool. - Deny web access and remote MCP tools the task does not need. - Use a local Git remote, or no remote at all. - Check where the package manager fetches from. - Account for telemetry and operating system traffic. - Inspect outbound traffic when the requirement calls for proof. Say "local model inference" when that is all you have verified. "Offline workflow" is a claim about every line above, not about where the weights sit. ## How Cursor's Local Path Differs OpenCode can call Ollama directly on localhost. Cursor's custom-endpoint route passes through Cursor's backend, so local weights alone do not make that workflow offline. You override the OpenAI base URL in settings and point it at Ollama or LM Studio, both of which expose an OpenAI-compatible endpoint. Three catches follow, and all three are real: 1. Cursor's backend runs sandboxed and cannot see localhost, so it needs a public HTTPS tunnel to reach your machine, and the request still travels out through Cursor and back. 2. A custom endpoint can supply standard chat models, while Tab autocomplete still uses Cursor's built-in models. 3. Cursor can send more context than a small Ollama default can hold, so raise the context only as far as your model and hardware allow. | Local models | OpenCode | Cursor | | How | Native provider config | Base URL plus a tunnel | | Truly offline | Only with all external paths disabled | No; routes via its backend | | Autocomplete | Requires a separate editor tool | Cursor's built-in models | | Context default | Set by the model and runtime configuration | Depends on the request and endpoint limit | One route people try does not carry across. Ollama's own VS Code path runs through GitHub Copilot Chat, which Cursor does not ship because it replaces it with its own AI, so the base-URL override is the method that works. The wider head-to-head, including pricing, the agent layer, and who each tool suits, is in [Cursor vs OpenCode](/ai-coding-tools/comparisons/cursor-vs-opencode/). ## Start Small and Verify the Whole Path OpenCode with Ollama is a sound fit for private routine work when a smaller model can follow the tool protocol, hold enough context, and finish your repository test at a useful speed. The setup is easy to connect, but the model and hardware decide whether it works as an agent. Start with one model, a 64K context only if the machine can hold it, and a narrow task with a known test. Keep a hosted provider for jobs that exceed that setup, and describe privacy in terms of the whole configured workflow rather than the location of one model. ## FAQ ### Does OpenCode work with Ollama? OpenCode connects to Ollama through either of two documented paths: the named Ollama provider, or a manual OpenAI-compatible block pointing at `http://localhost:11434/v1`. Prefer the manual block when you want the model list and its labels committed alongside the repository. Neither path invents a model, so pull it in Ollama first. ### Does a local model need an API key? Local Ollama models do not need an API key: Ollama's API at `http://localhost:11434` does not require authentication. The question is per destination rather than per tool, so one OpenCode session can still need credentials for Ollama cloud models, remote tools, or Git hosting. Check which provider is active before assuming no key is involved. ### Why do OpenCode tool calls fail with Ollama? OpenCode tool calls can fail with Ollama when the model does not follow tool syntax or the configured context window is too small. Raise `num_ctx` first, since OpenCode suggests 16K to 32K and Ollama's coding-agent guidance recommends at least 64K. If the same call still fails at the larger window, the model may be the problem rather than the context window. ### Can I run Kimi K3 or GLM-5.2 on a laptop? Full Kimi K3 and GLM-5.2 models are not practical laptop choices: Moonshot recommends 64 or more accelerators for Kimi K3, and GLM-5.2 carries 753 billion parameters. The test is not whether a quantized file fits on disk, but whether the agent finishes your repository task at an acceptable speed. Use hosted access and keep the local slot for a smaller tool-use model. ### Is a local model in Cursor really local? A model used with Cursor can run inference locally, but Cursor's request path still passes through its backend. Cursor documents that requests still pass through its backend even when you supply your own endpoint, and that backend cannot reach localhost, so the setup needs a public HTTPS tunnel. If your reason for going local is a data rule rather than cost, that request path is the thing to check first. --- ## Running Many OpenCode Sessions at Once URL: /ai-coding-tools/opencode/session-fleets/ Cluster: AI Coding Tools > OpenCode Run separate OpenCode sessions for independent tasks, and give simultaneous edits separate working directories. Each session has its own conversation; it does not automatically coordinate file changes with the others. A persistent server can let another client reconnect, while a headless script can handle repeated jobs. I would start with two tasks that touch different files and a clear review limit. More sessions save time only when their output arrives at a pace you can check. Version scope: The commands, configuration examples, and permission defaults below describe OpenCode 1. OpenCode 2 supports much existing configuration, but installation, server workflows, and some defaults differ. Read the [V2 migration guide](https://opencode.ai/v2/docs/migrate-v1/) before converting a setup. Both versions use the same `opencode` command, so their default installations can replace one another. Before adding parallel sessions, make sure the underlying tool fits your work. Our [coding tools guide](/ai-coding-tools/) compares OpenCode with the main alternatives. For teams sharing a gateway or remote host, the [OpenCode enterprise guide](/ai-coding-tools/opencode/enterprise/) explains the deployment and policy questions to settle first. ## When Should You Use Separate Sessions or Worktrees? OpenCode users should use separate sessions for independent conversations and separate worktrees or checkouts for concurrent edits. Use a shared server when clients need to reconnect to ongoing work. These solve different problems. Conversation separation does not protect files, and a worktree does not isolate credentials or network access. ## When Do You Need Multiple OpenCode Sessions? Multiple OpenCode sessions are useful when independent tasks can progress at the same time or when you want separate model attempts on one task. Repeated or scheduled jobs may need automation instead of more interactive sessions. - Waiting: a long refactor blocks you while a small fix sits untouched. - Independence: two tasks touch different parts of the repository. - Comparison: you want the same task attempted by two different models. - Repetition: the same mechanical change applies across many packages. - Scheduling: a job should run without a person sitting in front of it. Only the last two genuinely want automation. The first three want a second window and a clear rule about which files each session may touch. Reaching for orchestration when the real need is a second terminal adds machinery you then have to maintain. ## Use the Client and Server Split OpenCode runs as a client talking to a server rather than as a single terminal program. That split is what makes a fleet possible at all, because the session lives with the server and the client is just a view onto it. A plain `opencode` run starts both, so persistence depends on a server you keep alive. Three things follow from that, and each one is a capability you can use or ignore: - Several sessions can run against one project at the same time. - A client can attach to a running server from another machine, once you have secured it. - A session remains available after a client disconnects while the OpenCode server keeps running. Keep one project per server where you can. Mixing several repositories behind one server makes it harder to reason about which permission rules and which environment a given session inherited. OpenCode 2 differs: it normally discovers or starts a shared background service for your user. Its `--standalone` and `--server` options select a private or explicit server. The `serve`/`attach` examples below describe the V1 workflow; see the [V2 CLI guide](https://opencode.ai/v2/docs/cli/) before adapting them. ## Give Each Session Its Own Worktree This is the step most fleets skip, and it is the one that decides whether parallel work is useful or a merge problem. A git worktree is a second checkout of the same repository in its own folder, sharing one history. Two sessions editing the same working tree can overwrite or conflict with changes because their conversations do not automatically coordinate who owns each file. Separate worktrees turn that silent conflict into an ordinary merge you handle at the end. | Isolation | Cost | Use When | | One working tree | None | One session only | | Git worktrees | Disk, one branch each | Parallel edits, one repo | | Separate clones | Full copy each | Different dependencies | | Containers | Setup and images | Untrusted commands | Name the branch after the task, not after the session number. When you come back to four running jobs an hour later, the branch name is the only label that still tells you what the agent was asked to do. ## Drive a Session from a Second Device Because the client attaches to a server, you can start a long job at your desk and check it from a laptop or a phone. That is genuinely useful for jobs measured in tens of minutes. Get the security order right, because the default is not what people assume. The server binds to `127.0.0.1` and ships with no authentication at all, so anything that can reach it can run commands on that host. OpenCode's documented protection is `OPENCODE_SERVER_PASSWORD`, which turns on HTTP basic auth for `opencode serve` and `opencode web`. The username defaults to `opencode` unless `OPENCODE_SERVER_USERNAME` overrides it. That is the exact invocation its server page shows: ``` OPENCODE_SERVER_PASSWORD=your-password opencode serve ``` The variable is the only documented switch for this. There is no password flag on `opencode serve`, and the server still listens on `127.0.0.1` at port `4096` until `--hostname` or `--port` changes it. Prefer SSH port forwarding for cross-machine access, so the listener stays on localhost and your existing SSH keys do the authenticating. Exposing the server on a network is the riskier path: basic auth sends the password and every prompt unencrypted, so it also needs TLS in front of it, such as a reverse proxy, with the password set before anything else. Forwarding is two commands on the second device, one per terminal. The first command is standard OpenSSH port forwarding, not an OpenCode feature; the second attaches a client to the forwarded port rather than to a network address: ``` ssh -N -L 4096:127.0.0.1:4096 you@your-dev-machine opencode attach http://127.0.0.1:4096 --password your-password ``` Nothing on the dev machine changed for that to work, which is the point. The server kept its localhost bind, and SSH carried the encryption that basic auth does not. The `--password` flag is how `attach` presents the server's basic-auth credential from the second device, since the environment variable you set in the server's terminal does not exist there; it is only needed when the password is set. A second device is for watching and answering approval prompts. Writing a fresh multi-file task on a phone keyboard is how vague prompts get sent, and a vague prompt costs more to review than it saved to type. ## Automate Repeated Tasks with Headless Runs The repetition case is the one that deserves a script. When the same mechanical change applies across many packages, a loop that runs OpenCode once per package is more reliable than one session asked to remember twelve directories. The command for that is `opencode run`, which OpenCode documents as its non-interactive mode: it takes the prompt as an argument and exits when the run finishes. ``` opencode run "Update this package's lint config to match the repo root." ``` Four of its flags matter once a script owns the run. `--agent` picks which agent handles it, `--model` pins the provider and model, `--format json` gives output a script can parse, and `--auto` approves permission prompts so nothing waits for a human who is not there. With `--auto`, requests that would ask for permission are approved automatically; explicit deny rules remain enforced. Review those rules and the process's operating-system access before an unattended run. Three rules keep a scripted fleet from becoming a liability: 1. Run each unit against its own branch, so a bad run is one branch to delete. 2. Set permissions explicitly for the script's user, never inheriting your interactive setup. 3. Fail loudly on the first unit before letting the loop touch the rest. The following loop runs OpenCode sequentially, one package after another, and stops on the first nonzero exit. It demonstrates batch automation, not simultaneous sessions: ``` {`for pkg in packages/*/; do opencode run --agent build "Update the lint config in $pkg to match the repo root." || exit 1 done`} ``` That stops at the first nonzero process exit. A successful exit does not prove the requested edit is correct or that tests passed; add the package's actual validation command before advancing. It does not give you the first rule, and it is worth being clear about why: every run lands in the same working tree, so uncommitted changes from one package are still sitting there when the next one starts. Branch-per-unit therefore needs a commit, a stash, or a separate checkout between runs. Add that isolation before using the loop for independent tasks. Permission rules and bounded loops are their own subject, and the [advanced OpenCode guide](/ai-coding-tools/opencode/advanced/) covers the `allow`, `ask`, and `deny` patterns a scripted run should carry before it runs unattended. ## Add Orchestration Only When You Need It [Oh My OpenCode](https://github.com/code-yeongyu/oh-my-openagent) is a community extension, not an OpenCode product, and it adds a layer that routes work across a team of specialised agents. It is mid-rename to Oh My OpenAgent, so expect both names in the wild. Adopt it when you have already outgrown a shell loop, not before. The honest test is whether you can name the routing decision the orchestrator makes that your script cannot. If you cannot, the script is still the smaller thing that works. OpenCode's own subagents cover a lot of ground first: General for parallel multi-step work, Explore for read-only codebase search, and Scout for external docs and dependency research. All three are invoked from inside one session. Two mechanisms do that, and the [OpenCode agents page](https://opencode.ai/docs/agents/) documents both. A primary agent delegates to a subagent on its own, based on that subagent's description, and you can call one directly by mentioning it in the prompt, as in `@general help me search for this function`. Primary agents are the separate case. `Tab` cycles between Build and Plan inside a session, and `--agent` names the agent for a scripted `opencode run`. ## Keep Review from Becoming the Bottleneck A fleet moves the constraint. Generation stops being the slow part, and reading diffs becomes it. Three sessions producing changes faster than anyone reads them just builds a backlog with extra token spend attached. - Cap the fleet at the number of diffs you will actually read today. - Give each session an acceptance test it must pass before you look. - Watch the cost meter per session, not just the monthly total. - Stop a session that has retried the same failure twice. When a session repeats a failure, inspect the error and narrow the task before restarting it. ## How Does OpenCode Compare with a Managed Agent Interface? Cursor's Agents Window presents parallel tasks in one interface and supports several execution locations. OpenCode 1 gives you client/server and headless tools to assemble a workflow around your own environment. Compare the specific execution mode, isolation, and scripting support you need instead of assuming every commercial product uses only a vendor cloud. I favor a small terminal setup when it is already easy to supervise. A task board becomes useful when status and review, rather than model access, are what slow the work. The broader comparison is in [Cursor vs OpenCode](/ai-coding-tools/comparisons/cursor-vs-opencode/). ## Run the Smallest Fleet That Works Two sessions on separate worktrees, each with a task you could describe in a sentence, covers most of what a fleet is actually for. Add a third only if you have another independent task and spare review capacity. Use a script when the work is repetitive rather than merely parallel. Start from the [OpenCode overview](/ai-coding-tools/opencode/) if the basics are not in place yet, and read the [advanced guide](/ai-coding-tools/opencode/advanced/) before any session runs without someone watching it. ## FAQ Every OpenCode command, flag, and environment variable shown on this page was checked against OpenCode's own CLI, server, and agents documentation on August 10, 2026. The one command here that is not OpenCode's, the SSH tunnel, follows the OpenSSH manual page instead. The client and server split, the subagent trio, and the fleet guidance were checked on August 9, 2026. ### How many OpenCode sessions can I run at once? OpenCode does not publish a fixed session cap; the practical limit is your machine, your provider's rate limits, and how many diffs you can review. Start with two sessions. Add a third only when you have another independent task and enough capacity to review the extra output. ### Do parallel sessions share context? Parallel OpenCode sessions keep separate conversation and compaction histories and do not automatically coordinate edits. Sessions using the same working directory may still read files changed by one another, so give concurrent edits separate worktrees or checkouts. ### Does OpenCode isolate agents from each other? Separate OpenCode sessions do not provide a security boundary between agents. Git worktrees and separate checkouts keep edits in different folders, but do not restrict access to credentials, other files, or the network. Those restrictions depend on operating-system permissions or appropriately configured containers. ### Can I check on a run from my phone? Developers can check an OpenCode run from a phone by connecting a client to the server hosting the session. The server binds to localhost and has no authentication by default, so prefer SSH port forwarding rather than changing the bind address. Exposing it on a network needs OPENCODE_SERVER_PASSWORD and TLS in front of it, because basic auth alone sends everything unencrypted. ### Is a fleet cheaper than one session? Running multiple OpenCode sessions does not automatically make the same workload cheaper or more expensive. Total model cost depends on usage and provider pricing; duplicated context and extra retries can increase it. Parallel sessions can finish independent tasks sooner while consuming your usage allowance faster. ### When should I not run a fleet? Developers should avoid running multiple OpenCode sessions in parallel when tasks touch the same files, output exceeds their review capacity, or permissions are still being configured. A single well-scoped session beats three sessions producing changes nobody has read. --- # AI Code Review ## Complete Bugbot Review (2026) URL: /ai-code-review/bugbot/ Cluster: AI Code Review > Overview & AI Code Review BugBot is Cursor's pull-request reviewer for teams that want another pass over changed code before merge. It comments on potential bugs, security issues, and code-quality problems, then offers links that can send the finding into Cursor or Cursor's web agent. Bugbot suits teams that want review findings close to Cursor's repair tools. The convenience is real; so is the cost of rerunning reviews on every push. Start with a few representative changes, then decide whether automatic review earns its place. Our [AI code review tool comparison](/ai-code-review/) covers the alternatives. ## BugBot at a Glance Bugbot’s pricing and usage limits can change. Check your account dashboard for the terms that apply to you. | Area | Current Position | Buyer Check | | Review method | Pull-request diff analysis | Cross-file blind spots | | Triggers | Automatic or comment | Spend under automation | | Billing unit | Usage per run | Runs each month | | May 2026 estimate | $1.00 to $1.50 average run | Your own run costs | | Effort levels | Deeper review costs more | Risk-based policy | | Rules | Repository BUGBOT.md files | Rule scope and precedence | ## How Does Bugbot Review a Pull Request? Cursor describes BugBot as a tool that analyses pull-request diffs, leaves explanations and suggested fixes, and can run automatically on each update or from a comment trigger. The documented manual comments are `cursor review` and `bugbot run`. A finding should lead to a small investigation. Read the changed code and the caller or consumer it affects. Try to construct the state that would fail. Check which cases the existing tests cover and which they miss. Then decide whether to fix, dismiss, or turn the observation into a repository rule. This is ordinary code review work, and BugBot only makes the possible failure easier to notice. That distinction matters when a comment arrives with a confident explanation. A plausible comment can still miss a guard in another file or misunderstand a deliberate trade-off. Require the same evidence you would require from a human reviewer: a reachable path, a broken invariant, or a test that demonstrates the failure. ## How Do You Set Up Bugbot? BugBot connects through the Cursor dashboard and supports Origin, GitHub including Enterprise Server, GitLab including self-hosted installations, Bitbucket including Data Center, and Azure DevOps Services. Connect the provider with the required administrative permissions, then check which repositories have review enabled. GitHub connections can enable reviews for granted repositories automatically; the other providers need repository-level activation. 1. Choose one repository with an active pull-request habit. 2. Confirm which people and repositories BugBot can access. 3. Start in mention-only mode while you assess the findings. 4. Review results on several ordinary application changes. 5. Enable broader automatic coverage only where findings justify it. Use the first run to see which bugs Bugbot can find. Choose a pull request that changes authentication, data ownership, an external API, a migration or error handling. Small content edits and lockfile refreshes can wait. These changes give Bugbot something useful to check. I paused BugBot across my repositories on July 19, 2026 because of Cursor usage limits, and it is now back in our review workflow. Our team uses it alongside Codex and Claude Code across our projects. The pause is still a good reminder to check the dashboard's current allowance before turning on automatic reviews across a busy organisation. ## How Do You Write Bugbot Review Rules? Write Bugbot review rules in `.cursor/BUGBOT.md` files, naming the risky paths and behaviour the reviewer should check. Cursor says the root file is always included, and nested files apply when the review reaches their directory. This lets a repository state one project-wide invariant while giving an API directory its own security and validation guidance. ``` {`# .cursor/BUGBOT.md - Check every write preserves the authenticated tenant boundary. - Do not comment on generated clients under src/generated/. - Flag a changed public API when its contract test is absent. # src/api/.cursor/BUGBOT.md - Trace authorisation before every database read and write. - Check validation rejects unknown fields before persistence.`} ``` Name the unsafe operation and the evidence that would prove a defect. For tenant security, the boundary is the authenticated organisation, the operation is a database write, and the evidence is the authorisation path. Start with failures the team has seen and paths that are intentionally generated or experimental. Rules can create noise when they restate broad preferences, contain exceptions nobody can test, or ask the reviewer to enforce an entire style guide. Remove a rule that repeatedly yields low-value comments. Add rules for concrete defects you can explain and reproduce. A few specific rules are easier to maintain than a second style guide. ## How Do You Run Bugbot Manually? On a provider that supports comment triggers, run Bugbot manually by commenting `bugbot run` on a pull request after the implementation and tests are ready for inspection. Bitbucket Data Center supports automatic reviews only. Add `verbose=true` if troubleshooting is needed; Cursor documents verbose triggers for detailed logs and a request identifier. ## Check a Finding Before You Fix It A Bugbot comment needs a reachable failure, not just a plausible explanation. Read the affected function and its callers, check whether an existing guard prevents the problem, and reproduce the issue where practical. If the finding is correct, fix it and add a regression test. If it is wrong, explain which check or requirement protects the code. Do not rewrite working code merely because a comment sounds confident. ## Can Bugbot Fix the Bugs It Finds? Bugbot can hand findings to a coding agent for repair through Fix in Cursor links that open an issue directly in the editor and Fix in Web links that open the web agent. The handoff can save the context-switch of locating the file and copying the review comment. It does not remove the review step after a change has been proposed. Check the finding and the proposed fix in this order: 1. Confirm the finding describes a reachable defect. 2. Send the finding to the chosen repair tool. 3. Inspect the proposed diff before accepting any part of it. 4. Add or adjust a test that proves the repaired behaviour. 5. Request another review only when the fix changes the relevant path. This keeps the convenience of an agent handoff without converting a review comment into an automatic code change. If the suggestion is wrong, dismiss it with a reason and return to the pull request. There is no need to open a repair task. BugBot also offers optional Autofix, which starts a Cloud Agent and can push a repair according to your settings. That is a separate, write-enabled workflow from opening a Fix link. Autofix requires available usage and storage, uses Cloud Agent billing, and should leave the resulting patch subject to your normal checks. ## How Much Does Bugbot Cost? Bugbot charges usage against your account's available credit. Teams use prepaid credit, with on-demand spending retained on some legacy plans. Review effort, pull-request size and repeat runs affect the bill; Autofix adds Cloud Agent usage. Cursor estimated US$1.00 to US$1.50 per run in its May 2026 announcement. That historical average is not a current fixed price. Use the dashboard's actual charges when setting a budget. Low, Default, High and Smart effort let you choose how much work a review does. Higher effort can help on a difficult change, but it should earn the extra spend through useful findings. ## Set a Budget Before Automatic Review Choose mention-only, once-per-pull-request or automatic review to match your update frequency. A busy branch can trigger several reviews before the author has finished a single fix. - Set a spending limit and name the person who can change it. - Track review effort, reruns, confirmed defects and time spent dismissing incorrect comments. - Before raising the cap, check which repositories and triggers caused the unexpected bill. The [review trial method](/ai-code-review/#roll-out-one-reviewer) gives you a consistent way to compare those costs with the value of the findings. ## What Are Bugbot’s Main Limitations? Bugbot can miss defects, raise incorrect findings and generate variable usage costs. Its repair options also differ between source-control providers. Autofix can create a new branch or commit to the existing branch on GitHub and Origin; GitLab and Bitbucket Cloud support existing-branch fixes. Bitbucket Data Center supports automatic review only on Teams and Enterprise, without comment triggers or Autofix; Azure DevOps also has no Autofix. Check permissions, review settings and billing for your provider before enabling it. Keep responsibilities clear. A human reviewer owns the merge decision. Tests and static checks remain separate gates. Judge Bugbot by the real problems your team fixes after reading its comments. For more configuration options, read the [CodeRabbit guide](/ai-code-review/coderabbit/). For another codebase-context approach, read the [Greptile guide](/ai-code-review/greptile/). ## My Verdict Choose Bugbot if your team uses Cursor and can track what each review costs. Use it on code where a bug would matter, write `BUGBOT.md` rules for failures that can actually occur, and cap automatic spending in your account. If the cost stops you running the reviews you need, compare another tool. ## Frequently Asked Questions ### Is Bugbot included with Cursor? Bugbot uses Cursor usage-based billing, so a Cursor subscription should not be treated as unlimited Bugbot review access. Teams draw from prepaid credit or legacy on-demand spending. Check your account allowance and spending controls before enabling automatic reviews. ### Does Bugbot review every pull request? Bugbot reviews pull requests according to the enabled repositories and trigger settings. Automatic review, mention-only review and once-per-pull-request modes provide different coverage. Check those settings before assuming a new pull request will receive a review. ## Keep Reading - [AI code review hub](/ai-code-review/) - [CodeRabbit guide](/ai-code-review/coderabbit/) - [Greptile guide](/ai-code-review/greptile/) - [CodeRabbit vs BugBot](/ai-code-review/comparisons/coderabbit-vs-bugbot/) --- ## Complete Guide to Claude Code for PR Review (2026) URL: /ai-code-review/claude-code/ Cluster: AI Code Review > Overview & AI Code Review Claude Code can inspect a change before it leaves a laptop, inside a team-owned GitHub Actions job, or through Anthropic's managed pull-request service. The name is the same, but the models, permissions, billing and places your code is sent differ. Choose a local session for your own first pass, the Action for a workflow you control, or managed review for shared GitHub feedback without maintaining that workflow. What I like about a terminal agent like Claude Code for review work is that project instructions, hooks, and agent workflows help keep the work consistent with repository rules. Give Claude specific checks to make, and have a person verify the findings before release. For dedicated bots and other agent options, see the [AI code review guide](/ai-code-review/). ## Claude Code Review at a Glance Claude Code review paths. | Path | Runs In | Billing | Trade-off | | Local review | Claude Code session | Plan or API usage | Manual and private | | Claude Code Action | GitHub runner | Chosen provider | Team owns workflow | | Managed Code Review | Anthropic service | Usage credits | Hosted verification; variable cost | ## Which Claude Code Review Option Should You Use? Use local Claude Code review to check your own changes, Claude Code Action when your team wants control over its CI workflow, and managed Code Review when a Team or Enterprise organisation wants Anthropic to run GitHub reviews. Ultrareview adds a separate cloud review option started from Claude Code. ### Managed Code Review Anthropic's managed-review documentation describes a service for Team and Enterprise organisations. An admin installs the Claude GitHub App, selects repositories, and chooses when reviews run. The service reads the diff and surrounding codebase, posts inline comments, and creates a Claude Code Review check. Its check concludes as neutral, or skipped when an automatic review has no file changes; it never concludes as a failure. Teams wanting a merge gate must parse the severity data in their own CI or keep an existing required approval. Managed review suits a pull request that needs surrounding repository context without the team maintaining a custom action. Charges depend on token usage, and Anthropic processes the code on its servers. Quick, Standard and Deep review levels trade depth against time and cost; Standard is the default. The hosted service reads code without building or running it. ### Claude Code Action The official Claude Code Action documentation says the Action runs on the team's GitHub Actions runner. It can review a pull request, answer questions, or perform specific coding tasks based on the event and prompt. Authentication can use Anthropic directly, Amazon Bedrock, Google Vertex AI, or Microsoft Foundry. Anthropic's current GitHub Actions guide also documents workload identity federation for short-lived credentials, which avoids storing a long-lived Anthropic API key. The Action gives a team more control over the runner, event, prompt, model provider, and token permissions. Your team must also maintain it. Someone must pin the action safely, review permission changes, prevent untrusted text from steering privileged work, and watch provider spend. ### Local Review Local review happens inside the same Claude Code environment used to inspect and change the repository. A developer can ask Claude to review the current diff, run the `/code-review` command before pushing, or focus a pass on one contract such as tenant isolation. `/review` is now an alias of `/code-review`, which takes an effort level, applies findings with `--fix`, and posts them to a GitHub pull request or GitLab merge request with `--comment`. Anthropic also offers ultrareview, a research-preview cloud pass started with `/code-review ultra`. It runs a fleet of reviewer agents for roughly 5 to 10 minutes and independently verifies each finding. Pro and Max accounts get three one-time free runs, and later runs typically cost $5 to $25 in usage credits. A local `/code-review` pass is the smallest trial because it adds no GitHub App and no new organisation-wide trigger. Without `--comment`, the command returns findings to the developer rather than posting them to the pull request. Local here describes the workflow, not where model inference runs. Local review also has the widest variation. The result depends on the selected model, session instructions, available tools, permission mode, and context already in the conversation. Record the exact diff and review request if the team wants to compare runs fairly. ## How Does Managed Claude Code Review Work? Managed Claude Code Review uses several agents to inspect the diff and nearby code in parallel. Each looks for a different failure class. A verification stage checks candidate findings against the code, removes duplicates, ranks the rest, and posts surviving items by severity. Anthropic currently labels findings Important, Nit, or Pre-existing. Important means a bug worth fixing before merge, while Nit covers a minor issue. Pre-existing marks a problem already present in the repository so the current pull request does not take false blame. The service can run once when a pull request opens, after every push, or by comment. Anthropic's docs list these triggers: | Review Trigger | Run Behavior | Billing and Rerun Effect | | Initial PR creation | Single opening review | One billed run | | Every push | Reviews each update | Multiplies by pushes | | `@claude review` | Single requested review | No push subscription | | `@claude review once` | Same one-shot behaviour | No push subscription | | `@claude review always` | Reviews and subscribes | Future pushes run | Since July 2026, the bare command is a safer one-shot control for long-running pull requests. Use `@claude review always` only when later pushes should trigger fresh reviews and charges. The explicit `once` form remains an alias for the bare command. ## How Much Does Claude Code Review Cost? Claude Code review costs depend on how you run it. Local sessions use your plan or API allowance, the Action uses your configured provider and runner, and managed review charges separate token-based usage credits. Ultrareview has its own cloud-review charges. | Billing Path | Current Price | Applies to Workload | | Claude Pro | About $17/month, billed as $200 upfront annually; $20/month, billed monthly | Local Claude Code | | Claude Max | From $100/mo | Higher plan usage | | Managed review | Variable token-based usage credits | Each review run | | Ultrareview | $5 to $25 after free runs | Each cloud review | Claude Code Action follows the chosen provider's model and runner billing. Local plan usage competes with the developer's other Claude work. During the trial, track the full review bill and time spent checking comments. A subscription price and the cost of one managed run are not directly comparable. ## How Do You Configure Claude Code Review Rules? Configure Claude Code’s local review with `CLAUDE.md` and managed review with `REVIEW.md`. CLAUDE.md supplies broader project context. `REVIEW.md` supplies managed-review instructions at the repository root, with optional subdirectory files for more specific checks. Anthropic gives `REVIEW.md` directly to the agents that find and verify findings, and the agents that rank and report them consult it before settling severity. Put the rules you want enforced directly in the file. A compact backend example can look like this: ``` # Review instructions ## Important findings - Unscoped tenant queries - PII in logs or errors - Migrations without a rollback path ## Skip - Generated files and lockfiles - Formatting and type errors caught by CI ## Evidence - Cite the source path and call chain - Suppress a claim that cannot be verified ``` Each rule describes a requirement that should still apply after functions are renamed. The skip list protects reviewer time from low-value generated content. Requiring evidence makes the agent show how files affect each other before a developer investigates. Set a nit cap if prose, config, or tests attract endless polish. Another useful rule suppresses fresh nits after the first review so a one-line fix does not start a new style loop. Keep business and architecture context in `CLAUDE.md`, where it also helps ordinary Claude Code sessions. Managed review reads `REVIEW.md` from the base or default branch, never from the pull request's proposed changes. Merge instruction changes first. Severity-lowering, exclusions and nit caps depend on the protected-default-branch rules in Anthropic's documentation; a file in an unprotected branch is not an equivalent control. Local `/code-review` reads `CLAUDE.md`, not `REVIEW.md`. ## What Can Claude Code Review Catch? Claude Code Review looks for logic errors, security flaws, broken edge cases, and regressions that need surrounding code. A changed permission check may look safe in the diff while a caller passes the wrong identity. A migration may compile while an older worker still expects the prior schema. The extra check on each finding is a key reason to consider managed review. A plausible comment must survive another inspection step before it reaches the pull request. That reduces the chance of developers spending time on an unchecked guess, although Anthropic does not promise that every surviving comment is correct. Local review can use tools from the active session. It may run a targeted test, inspect a type, search all call sites, or compare behaviour with a requirement file. Name the expected behaviour in the prompt and ask Claude to cite the code that supports its finding. ## What Are the Limits of Claude Code Review? Claude Code Review can miss unstated requirements, misread code and produce incorrect findings even after verification. It can trace a pricing calculation and miss that the formula violates a sales contract. It can judge a local change safe even when that change breaks an operational runbook stored elsewhere. Managed review carries queue time and variable cost. Every-push triggers can turn a busy pull request into several bills. A failed run ends neutrally, and Anthropic retries only some interrupted reviews on its own, so the lack of a blocking check cannot be read as a clean review. `REVIEW.md` can create its own noise when it grows into a second handbook. Conflicting rules, stale path names, and long lists weaken the checks that matter. Review the file when responsibilities move between modules, and delete rules your CI checks now enforce. ## Where Does Claude Code Review Process Your Code? Managed Claude Code Review processes code on Anthropic infrastructure and is unavailable with Zero Data Retention, HIPAA configuration or customer-managed encryption keys. It is not covered by Anthropic's Business Associate Agreement. The GitHub App asks for repository contents plus pull-request and issue permissions. Start with selected repositories and confirm the organisation's retention and access settings. The Action runs on a team runner, yet model requests still leave for the configured provider. Review the runner image, outbound network, secrets, logs, checkout behaviour, and token permissions. Workload identity can remove a stored API key. Your team still owns the other controls. Keep review read-only during the first trial. A workflow that can post a finding needs less authority than one that can push a fix. Separate those jobs, and require a developer to verify the issue before any write-enabled task runs. Security prompts should name the authorised repository, defensive purpose, expected evidence, and excluded actions. That context helps the model understand the task and gives the human reviewer a clear standard for rejecting an unsafe detour. Pull-request text, source comments, fixtures, and generated files can contain instructions that compete with the workflow prompt. Treat them as untrusted data, keep privileged rules in reviewed configuration, and require tool evidence for claims that cross file or service boundaries. ## Test the Review Mode You Plan to Use Use the [hub's four-week method](/ai-code-review/#roll-out-one-reviewer) for the common sample and outcome labels. Start locally on one clean diff and one known defect, recording the model, instructions, files available, findings, and checks run. The next step is choosing one shared review mode. A team that already owns GitHub Actions can try the Action with read-only permissions. A Team or Enterprise organisation can enable managed review on one repository with `@claude review once`. 1. Limit scope: One repository and manual triggers. 2. Add rules: A few relevant checks in `REVIEW.md` for managed review, or `CLAUDE.md` for local review. 3. Measure: Verified defects, false positives, cost, and triage time. 4. Trigger check: Compare one-shot commands with `@claude review always` and record the extra push-triggered runs. Keep tests, static analysis, and required human approval in place throughout the pilot. Recheck `REVIEW.md` after several pull requests and remove any rule that produces repeated guesses. Record failed and neutral runs alongside successful ones. A review process that completes only on small diffs can look accurate while disappearing on the changes that need it most. Completion rate, queue time, and cost belong beside accepted findings in the rollout decision. ## My Verdict Claude Code is strongest when each review mode has a distinct job. Use `/code-review` for a private developer pass, the Action for a team-owned runner and provider, and managed review for costly pull requests that justify multi-agent verification. A short `REVIEW.md` sharpens the hosted review; accountable approval stays with a person. ## Frequently Asked Questions ### How long does managed Claude Code Review take? Managed Claude Code Review completion time varies with the review level, change size and verification work. Quick, Standard and Deep levels trade review depth against time and cost. Check the run status rather than treating an unfinished review as a clean result. ### Which model does managed Claude Code Review use? Anthropic does not name one fixed model for managed Claude Code Review, and its admin settings do not offer a model selector. Choosing a model in a local Claude Code session does not select the model for the managed service. ### Can Claude Code review run on a team runner? Claude Code Action can run a review on your GitHub Actions runner, while managed Claude Code Review runs on Anthropic infrastructure. The Action still sends model requests to the provider you configure; running the job yourself does not make model processing local. --- ## Complete CodeRabbit Review (2026) URL: /ai-code-review/coderabbit/ Cluster: AI Code Review > Overview & AI Code Review A pull request can be small enough to review quickly and still hide the change that matters: an error branch that drops a response, a permission check that moved one line too late, or a generated file that turns a useful review into noise. CodeRabbit is an AI code reviewer for that moment. It reads pull requests, posts a walkthrough and inline comments, and gives a team a configurable second pass before merge. CodeRabbit suits teams that want broad pull-request feedback and rules tailored to different parts of a repository. The trade-off is time spent tuning and checking those comments. For a narrower bug-finding tool, compare the alternatives in our [AI code review roundup](/ai-code-review/). ## CodeRabbit at a Glance CodeRabbit’s prices and usage limits can change. Check the current plan details before subscribing. | Area | Current Position | Buyer Check | | Review surface | Pull requests, IDE, CLI | Where your review happens | | Context | Repository and organisation config | Precedence between them | | Billing unit | Paid developer seats | Authors, not headcount | | Paid floor | $24/dev/month billed annually; $30 billed monthly | Billing basis chosen | | Rate limits | Five hourly reviews, Essentials | Release-day bursts | | Free entry | Summaries after the trial | Private repository needs | ## Who Should Use CodeRabbit? CodeRabbit suits teams that need pull-request reviews with path-specific instructions, shared configuration and feedback that carries into later reviews. It also offers IDE and command-line review. Its strongest practical feature is configurable repository context. A repository can keep a `.coderabbit.yaml` file beside its code, while an organisation can apply settings where a shared policy needs to win. That arrangement suits teams with several kinds of code. An API handler needs scrutiny for authorisation and input validation. A test directory needs attention on assertions and missing cases. A generated client usually needs an explicit review exclusion. Asking for all three things in one generic prompt creates muddy feedback. Short instructions tell the reviewer what to check in each directory. CodeRabbit also records team feedback as learnings, according to its documentation. Those learnings can reflect accepted and rejected suggestions, coding standards, and focus areas. Check the settings and saved preferences. Learning from comments does not prove that the reviewer understands your unwritten rules. ## How Much Does CodeRabbit Cost? CodeRabbit lists Essentials at US$24 per developer each month with annual billing, Team at US$48, and Advanced at US$72 on the same basis. Monthly billing is higher, while Enterprise uses negotiated terms. The free tier covers pull-request summaries plus IDE and CLI review, and public repositories can receive free pull-request reviews. Verify the live pricing page before approving a purchase. | Plan | Annual-Billing Price | Review Fit | | Free | $0 | Summaries, IDE/CLI, public repositories | | Essentials | $24/user/month | Full PR review, five per hour | | Team | $48/user/month | Eight per hour, custom pre-merge checks | | Advanced | $72/user/month | Adds continuous PR security review | | Enterprise | Custom | SSO, RBAC, audit logs, self-hosting | CodeRabbit bills only developers who create pull requests, with manual seat assignment also available. Estimate the bill from active authors, then include the work of reviewing the output. Pay for a higher plan only if its extra features catch problems or save your team time. ## How Do You Set Up CodeRabbit? Set up CodeRabbit by connecting your supported Git provider, granting access to the chosen repository and requesting a review on a representative pull request. Choose a change that crosses a boundary your team cares about, such as an API change with tests or an authentication change with a migration. A typo-only pull request tells you little about a reviewer. A huge refactor makes it harder to work out why the first review succeeded or failed. 1. Connect the repository through the provider supported by your account. 2. Run a review against a pull request with a clear behaviour to check. 3. Classify each comment: verified defect, useful question, duplicate, or noise. 4. Write down the files and risks that deserved better guidance. 5. Add only the configuration needed for the next review. This shows which comments actually helped. A long walkthrough can explain the change. It does not prove that the reviewer found a bug. Keep a small record of which comments led to a changed test, a corrected implementation, or an informed dismissal. That record tells you whether the tool is saving review time. CodeRabbit's configuration reference lists file patterns, path filters, and review instructions. Check the current reference before each edit, because valid options and defaults change. ## How Do You Configure CodeRabbit? Configure CodeRabbit in `.coderabbit.yaml`, using path instructions for review guidance and path filters for exclusions. A request to “review carefully” leaves the tool with the same ambiguity as a hurried human reviewer. A request to trace tenant ownership from a route to its database query gives it a specific task. Keep instructions short enough that a reviewer can later tell whether a comment came from a genuine rule or a vague preference. ``` {`reviews: path_instructions: - path: "src/api/**" instructions: "Trace authorisation and tenant ownership to every data write." - path: "src/generated/**" instructions: "Do not review generated output." - path: "src/**/*.test.ts" instructions: "Check changed behaviour has an assertion and a failure case."`} ``` The example uses documented `path_instructions`: a glob pattern plus additional guidance for matching files. Its generated-file instruction asks the reviewer to skip output; use `path_filters` when you need to exclude those paths from review. It is a pattern to adapt for your repository. CodeRabbit's current schema also has path filters, guideline-file patterns, and organisation-level overrides. [CodeRabbit’s configuration reference lists the available settings.](https://docs.coderabbit.ai/reference/configuration) Keep generated output and lockfiles out unless their contents are the review subject. Tell the reviewer where project rules already live instead of duplicating a long manual. CodeRabbit documents default guideline-file patterns that include common agent and instruction files such as `AGENTS.md` and `CLAUDE.md`. That can make a repository's existing rules available to review, provided those rules are maintained and scoped. ## Check Which Configuration Takes Precedence CodeRabbit's organisation overrides can take precedence over repository settings, and some arrays merge by key. Check the effective configuration before assuming a local edit has changed the review. Try a rule against a change that should trigger it and a safe exception that should not. Remove instructions that repeatedly produce irrelevant comments. Keep the rules specific enough that another maintainer can explain why they exist. ## Read Comments as Review Leads Treat each inline comment as a possible problem to investigate. Open the called code path behind the review comment. Identify the input that would reach it. Check whether a test proves the concern or disproves it. A suggestion that describes a specific input, state, and result is much easier to validate than a broad claim that code “could be improved.” Use a response convention during the pilot. Mark verified defects with the test or reasoning that confirmed them. Explain dismissals in one sentence when the same pattern is likely to recur. Turn a repeated correct observation into a repository rule only after the team agrees it represents a durable requirement. Assign follow-up work rather than leaving unresolved comments for everyone to ignore. CodeRabbit offers a CLI for local Git changes, covering staged and unstaged edits to tracked files, which can bring a review pass into a local loop before a pull request exists. CodeRabbit’s plans page gives Free users three CLI reviews per developer per hour; Free pull-request access is summaries with a separate allowance. Paid CLI and PR allowances are five on Essentials, eight on Team and ten on Advanced, subject to rolling limits, fair-use rules and grandfathered terms. Eligible over-limit reviews cost $0.25 per reviewed file. Confirm the current CLI terms before making local review a required step. ## Measure Useful Findings, Not Comment Counts A useful CodeRabbit review finds a real defect or directs a person to an important question. A longer comment thread is not automatically a better review. Track confirmed defects, incorrect findings and the time spent investigating them. If an entire category of comments is repeatedly dismissed, tighten the relevant instruction or disable that category where the product allows it. ## Test CodeRabbit on Your Repository Use the [four-week review trial](/ai-code-review/#roll-out-one-reviewer) to compare useful findings with developer time and spend. For CodeRabbit, also check that organisation overrides, repository settings and learned preferences produce the configuration you intended. Include one known defect and one valid exception. A reviewer that finds the first but wrongly flags the second still needs tuning. ## Where CodeRabbit Fits in a Review Stack CodeRabbit is a configurable, broad review layer. It sits comfortably beside tests, type checks, security scanning, and human review because they check code in different ways. A test can prove expected behaviour during execution. A security scanner can match known patterns. A reviewer can ask whether a new behaviour violates a requirement that no automated test has expressed yet. We have used CodeRabbit to review pull requests across our projects, and it still earns a good word from us. In that workflow, we kept CodeRabbit advisory rather than letting it decide what merged. Leave the review running and inspect any concrete finding. Keep live checks and a human decision as the merge gate. Compare it with a focused bot when your team only wants bug-oriented feedback, or with an agent-assisted workflow when the priority is passing a finding into a coding agent. The [AI code review comparisons hub](/ai-code-review/comparisons/) and [BugBot guide](/ai-code-review/bugbot/) help place those choices without forcing one tool to perform every kind of review. ## Frequently Asked Questions ### Is CodeRabbit free for open source? CodeRabbit offers free reviews for eligible public repositories. Private-repository Free access provides pull-request summaries rather than the full paid review, alongside limited IDE and CLI review. Check the current eligibility and usage limits for your repository. ### Can CodeRabbit review local changes? CodeRabbit can review local Git changes through its CLI, including staged and unstaged edits to tracked files. Free accounts have a limited hourly CLI allowance; paid limits and overage options depend on the plan. A local command still uses CodeRabbit’s review service. ## My Verdict Choose CodeRabbit when you want broad review feedback and enough configuration to focus it on your repository’s risks. I would keep the useful comments, tune the repetitive ones and judge the result by defects prevented and time saved. ## Keep Reading - [AI code review hub](/ai-code-review/) - [BugBot guide](/ai-code-review/bugbot/) - [Greptile guide](/ai-code-review/greptile/) - [CodeRabbit vs BugBot](/ai-code-review/comparisons/coderabbit-vs-bugbot/) Facts and prices were checked against the official vendor pages on October 1, 2026. --- ## Complete Guide to Codex as a Code Reviewer (2026) URL: /ai-code-review/codex/ Cluster: AI Code Review > Overview & AI Code Review Codex can review local code, GitHub pull requests and GitLab merge requests. GitLab’s native integration is in beta. Local review lets a developer choose the scope and inspect the result privately, while GitHub review adds a shared, repeatable checkpoint to the team workflow. I run `@codex review` in our release process as a second opinion on the latest code before release. I care about when to trigger it, what its P0 and P1 filter leaves out, how repository instructions shape the result and how much permission it receives. For alternatives with different review coverage, see our [AI code review recommendations](/ai-code-review/). ## Codex Review at a Glance | Surface | Trigger | Context | Output | | Desktop Code Review view | Open the review view | PR or merge request | Your review, submitted | | GitHub cloud | Pull-request comment | PR and repository | P0/P1 review | | Local client | Developer prompt | Working tree | Private findings | | `codex-action` workflow | Workflow event | Configured checkout | Workflow output file | The managed GitHub review is the distinctive review product. OpenAI controls its review model and priority threshold, then posts findings through GitHub's normal review interface. The ChatGPT desktop app’s Code Review view provides summaries and diffs, and lets developers ask questions about a change. From that view a developer submits the review as Comment, Approve, or Request changes, and GitLab merge request support is in preview. Local Codex and `codex-action` offer more freedom, but the team owns the prompt, permissions, event filters, output publishing, and usage controls. ## Where Can Codex Review Your Code? Codex can review GitHub pull requests and GitLab merge requests, inspect local changes or run inside a custom GitHub Actions workflow. The desktop review view also helps you examine and submit a review yourself. These routes have different permissions and usage limits. ### GitHub Review Connect Codex cloud to a GitHub repository, then request a review with `@codex review` or enable automatic reviews in Codex settings. OpenAI's GitHub integration documentation says Codex posts a standard review and flags P0 and P1 problems by default. Manual review can also request P2 findings. Automatic cloud reviews no longer require a cloud environment, and `@codex security review` requests a security-focused pass. This mode suits a shared release checkpoint because the request, findings, replies, and follow-up commit remain attached to the pull request. A developer can verify a comment against the diff, answer with missing context, or start a separate Codex task to address a confirmed problem. The [native GitLab integration](https://learn.chatgpt.com/docs/third-party/gitlab) supports GitLab.com and configured self-managed or Dedicated instances in beta. Manual GitLab reviews include P0, P1 and P2 findings by default; automatic reviews focus on P0 and P1. Do not assume GitHub’s default threshold applies to every Codex review mode. ### Local Review Local review works earlier in the change. In the CLI, `/review` or the non-interactive `codex review` inspects uncommitted work, a branch diff, or a specific commit without changing the working tree. OpenAI's pricing page says these local reviews count toward general Codex limits, while Code Review usage covers GitHub runs. The prompt can name requirements, threat boundaries, and known invariants without placing those notes in a public review thread. Locally, you can review the diff, investigate a finding, change the code, run the repository checks and ask for a fresh pass. Preserve the original requirement throughout that loop because a model can improve an implementation while drifting away from the requested behaviour. Start with a specific diff that a developer can check. A staged diff exposes the proposed commit, while a branch comparison shows the complete change against its base. Name the base branch and expected behaviour so the review stays on the intended changes. Keep review and repair as distinct requests by asking Codex to report findings with file references, impact, and evidence first. After a developer confirms one, a new task can implement the smallest fix and run the relevant checks. That lets you check whether the reported bug exists before Codex changes the code. Local output needs a handoff when it affects team approval. Carry the confirmed finding, evidence, and resulting test into the pull request instead of citing an invisible private session. Shared reviewers can then evaluate the same facts without trusting a summary of what the model said. ## How Do You Request a Codex Review on GitHub? Request a Codex GitHub review by commenting `@codex review` on a pull request in a connected repository with code review enabled: ``` {`@codex review `} ``` You can add a specific focus, such as `@codex review for issues in the database migration`. OpenAI also documents automatic reviews for repositories that need consistent coverage. Manual triggering gives a team tighter spend control and a clearer review moment, especially when a pull request receives several small fix commits. Automatic mode is easier to administer after the review has earned a stable place in the workflow. Codex reads the pull-request change and applicable repository instructions, then publishes only findings that meet its managed threshold. The completed review can return without any published findings. A clean review means the model did not identify a P0 or P1 issue in the available context; tests, human review, and lower-severity engineering checks still have their own jobs. After a developer confirms the finding and approves a write-enabled task, a follow-up comment can hand the problem back to Codex: ``` {`@codex fix the P1 issue `} ``` That command starts a separate agent task. The repository connection must allow the task to write, and the resulting commit needs the same build, test, and review checks on the updated code as any other change. ## What Do P0 and P1 Mean in a Codex Review? In a Codex review, P0 means an urgent blocker and P1 means a high-priority defect that should be fixed promptly. Managed GitHub review reports these two levels by default, including security vulnerabilities, crashes, data loss and logic errors with a credible production path. Manual reviews can also request lower-priority P2 findings. The filter is intended to keep comments focused on serious defects; it does not guarantee every finding is correct. Naming preferences, cosmetic refactors, speculative abstractions, and routine style issues should stay out of the review. Deterministic formatting and type checks belong in CI, where the result is repeatable and cheaper. The same filter creates a deliberate coverage gap. Duplicated code, unclear boundaries, weak tests, confusing APIs, and accumulating maintenance cost may matter without reaching P1. Ask a human reviewer to check those design and maintenance concerns when they matter to the change. Every priority label also requires verification against the actual code. A plausible warning becomes actionable only after a developer confirms the execution path, checks the surrounding contract, and reproduces the risk where practical. Incorrect P1 labels should be dismissed with a reason so the team keeps severity language meaningful. ## Add Repository Rules with `AGENTS.md` Codex GitHub review follows applicable `AGENTS.md` guidance from the repository root and more-specific files covering the changed code. Put shared review rules at the root and service-specific rules near the service they govern. Review rules work best when they describe a costly failure and the accepted safe path. A short rules block looks like this: ``` ## Code Review Rules - Flag authorisation checks performed after protected data is read. - Require idempotency for retryable payment and webhook handlers. - Treat destructive schema changes without a rollback path as P1. - Leave formatting, imports, and generated files to CI. ``` These rules give Codex domain facts that the diff cannot supply. A nested billing service could add currency and retry rules, while an API directory could state compatibility guarantees. Keep the list short enough that each rule remains visible during review. Rules need maintenance when the architecture changes. Remove obsolete warnings, encode newly discovered failure modes, and test a changed rule against a representative pull request. An instruction file that preserves every historical concern eventually obscures the risks that still matter. ## A Trigger Flow from Daily Use In our release process, Codex review runs on the exact pull-request head. A fix changes the code. An earlier review does not cover those new edits. 1. Push the completed change and wait for the repository's deterministic checks. 2. Post `@codex review` as its own trigger comment. 3. Verify each finding against the current diff and project requirements. 4. Apply a confirmed fix as a normal follow-up commit. 5. Run the checks again and request a fresh review on the new head. Request another review when a fix materially changes the code or introduces a new risk. A comment explaining existing behaviour does not need a fresh paid run. ## Verify and Resolve Findings Start with the execution path Codex describes by opening the cited lines, tracing the relevant caller, and checking guards outside the diff. Compare the behaviour with the requirement or repository rule before accepting the severity. Reproduce the problem, find a failing test or show which documented requirement the code breaks. When the finding is correct, prefer the smallest change that removes the failure. Add a regression test where one can reproduce the defect deterministically. Then run the repository's normal checks and inspect the resulting diff before requesting another Codex review. When the finding is wrong, reply with the missing fact. The explanation helps the human reviewers and can reveal an instruction the repository should encode. Stable exceptions belong in `AGENTS.md`; one-off context can stay in the pull-request thread. A review with no findings still needs a recorded outcome. Confirm that Codex ran on the current SHA, inspect any failed or cancelled check, and keep human approval independent. An absent comment should never stand in for proof that the reviewer completed successfully. ## Use `codex-action` for a Custom Pipeline OpenAI's official Codex GitHub Action documentation says `codex-action` runs Codex inside a GitHub Actions workflow. It can read a prompt from workflow input, operate in a read-only sandbox, and write its final response to a file for a later step to process. This route supports event filters and prompts that the managed reviewer does not expose. A workflow could run only when authentication files change, inspect a migration plan, or compare a release diff against a checklist. The action runs Codex, but your team decides what it checks and how to use the result. Workflow security starts with the GitHub permissions granted to each job. Give the workflow read access when it only analyses code, keep untrusted pull-request content away from privileged credentials, and separate result publishing from agent execution. If a later job posts a comment, that job should consume the output through a narrow interface. Pin the action to a reviewed commit SHA and inspect future updates before changing that pin. An immutable reference protects the workflow from an upstream tag move, while a version comment can record the release that the SHA represents. ## How Much Does Codex Code Review Cost? Codex code review uses the allowance or credits available on your supported ChatGPT plan; it does not have one universal price per pull request. GitHub review usage and local review usage are accounted for differently. Local reviews count towards general Codex usage, while a custom API-based workflow incurs API charges. Record the usage shown after a representative review. Diff size, retrieved context, review frequency and your account terms affect the total. Automatic review can consume an allowance quickly on a busy repository. ## What Are the Limits of Codex Code Review? Codex GitHub review focuses on serious defects and can miss lower-priority design problems, unavailable context or bugs it fails to recognise. A clean review is useful feedback, not a guarantee. - Priority coverage: managed review intentionally omits most lower-severity maintainability feedback. - Available context: external services, production state, and unstated business rules can remain invisible. - Shared usage: automatic reviews consume the applicable review allowance or credits under your account terms. - Write access: asking Codex to fix a finding can move the task from read access to branch writes. - Variable findings: a rerun may phrase, prioritise, or discover findings differently. Give each check a clear job. CI runs repeatable tests, Codex looks for serious defects, and a developer decides whether to merge. Repository rules improve the search without transferring accountability to the model. ## Test Codex on Your Own Pull Requests Use the [hub's four-week method](/ai-code-review/#roll-out-one-reviewer) for the common sample and outcome labels. For Codex, record the reviewed head SHA, credits used, and whether an `AGENTS.md` rule contributed to each accepted or dismissed finding. Earlier findings can remain valid after a fix, but the review does not cover newly introduced changes. Re-run the relevant checks and request another review when the remaining risk warrants it. ## My Verdict Codex fits a release workflow that wants few comments and a high severity threshold. I encode costly failures in `AGENTS.md`, verify every P0 or P1 against the code, and rerun after a fix changes the SHA. Teams seeking broader maintainability feedback will need an additional review process. ## Frequently Asked Questions ### Which model does Codex code review use? OpenAI does not name a dedicated model for the managed GitHub reviewer in its review documentation. Models available for ordinary Codex tasks do not establish which model handles a managed review. ### Can Codex fix a finding after review? Codex can start a separate cloud task to fix a review finding when you request it in a pull-request comment, such as @codex fix the P1 issue. The task can change the branch only when it has the necessary write access. Check the resulting patch before merging. ### Can codex-action run a read-only review? OpenAI’s codex-action can run Codex in a read-only sandbox and write its result for a later workflow step to publish. The workflow still needs appropriately limited GitHub permissions, and any step that publishes comments needs its own permissions. --- ## Using Your Coding Agent as a Code Reviewer URL: /ai-code-review/coding-agents-as-reviewers/ Cluster: AI Code Review > Overview & AI Code Review Coding agents now review code in the same places where they plan, edit, and test it. That gives a team another way to inspect a pull request without buying a standalone bot first. Choose a local review for feedback before committing, or a managed review for comments the whole team can see. The [best AI code review tools](/ai-code-review/) guide compares the dedicated alternatives. I use Claude Code and Codex as parallel coding agents, and `@codex review` is part of my daily promotion flow. That experience makes the category useful to me, but it also exposes its limits. A review only earns trust after someone checks the finding against the code and the requirement. ## How Does a Coding Agent Differ from a Review Bot? A coding agent handles broader repository tasks such as editing and testing, while a dedicated review bot centres its workflow on reviewing changes. Its main jobs are automatic coverage, inline comments, team rules, dismissals, analytics, and integrations. A coding agent starts with repository work, then adds review as another task it can perform before or after a pull request exists. That wider job can provide more context. The agent may read project instructions, inspect files outside the diff, run tests, or trace a call path. The same reach can increase risk because a tool able to review and fix code may also hold branch write access, network access, secrets, or a broad repository token. Check where the review runs, not just which product runs it. A local terminal review has a different data path from a managed GitHub App, while a GitHub Action on your runner has different controls from a vendor-hosted task. Check those differences before comparing model names. ## Agent Reviewers at a Glance Agent reviewers by surface, cost and control. | Reviewer | Main Surface | Cost Shape | Useful Control | | [Codex for code review](/ai-code-review/codex/) | GitHub cloud or local CLI | Plan usage or credits | `AGENTS.md` rules | | [Claude Code for code review](/ai-code-review/claude-code/) | Managed, Action, or local | Plan, API, or per run | `REVIEW.md` for managed review | | [GitHub Copilot](/ai-code-review/github-copilot/) | GitHub and supported IDEs | AI credits plus Actions | Effort and repo instructions | | [BugBot](/ai-code-review/bugbot/) | Supported Git hosts and local Cursor reviews | Usage per run | Review effort | ## Where Can Coding Agents Review Your Code? Coding agents can review code in a local session, through a managed repository integration or in your own CI workflow. The available routes depend on the product. Each route changes who maintains the workflow, how much reviews cost and which code the reviewer can inspect. ### Codex Cloud and CLI Review OpenAI's Codex review documentation says the GitHub reviewer reads the pull-request diff, follows applicable repository rules, and posts P0 and P1 findings by default. Manual review can also request P2 findings. A developer can request it with `@codex review` or enable automatic review for new pull requests. The local route catches defects earlier in the development cycle. Codex can review a branch diff, a commit, or uncommitted changes before you open a pull request on GitHub. That makes it useful for a private first pass, while the managed GitHub review creates a shared record for the team. GitHub reviews consume the applicable Codex review allowance or credits. Local reviews use general Codex usage. Check the account meter for the route you actually use before estimating a monthly bill. ### Claude Code Review Claude Code splits review across a local command, the official GitHub Action, and Anthropic's managed service. The managed path is a Team and Enterprise service that sends several agents through the diff and surrounding code, then verifies candidate findings before posting them. Anthropic's managed-review page lists automatic review once when a pull request opens or after every push. Manual `@claude review` and `@claude review once` comments each run one review, while `@claude review always` also subscribes later pushes. Review depth is configurable as Quick, Standard or Deep. Managed reviews charge token-based usage separately from the plan allowance; deeper reviews generally take longer and cost more. The official Claude Code Action documentation says the Action runs on your GitHub runner and can authenticate through Anthropic, Amazon Bedrock, Google Vertex AI, or Microsoft Foundry. That route leaves the team responsible for the prompt, permissions, runner, and provider bill. ### GitHub Copilot Code Review GitHub's code review overview lists GitHub.com, GitHub CLI, GitHub Mobile, major IDEs, and an Azure DevOps preview. Its review effort settings include Lite and Balanced, with Balanced as the built-in default. More reasoning can increase both time and cost. Copilot uses a GitHub-managed mix of models, prompts, and system behaviour. The review surface does not let a developer select the model used by Copilot Chat. Billing combines AI credits for the model interaction with GitHub Actions minutes for agentic context gathering and tool use. Repository instructions, agent skills, setup workflows, and selected MCP servers can add project context. Copilot comments do not count as required approvals by default. An opt-in approval preview can satisfy approval rules when the required policies are enabled; new commits dismiss that approval. Teams can add automatic review through a ruleset, then keep a human reviewer and branch protections in place. ### Cursor BugBot Bugbot reviews changes on supported Git hosts, including GitHub, GitLab, Bitbucket, Azure DevOps and Origin, and offers local review through Cursor. Findings can lead to a separate fix task. Current billing uses credits, so review frequency and effort affect spend. Bugbot offers Low, Default, High and Smart effort. Autofix support varies by host and incurs additional usage. A review and a fix are separate jobs with different permissions. I previously paused Bugbot in my project workflow because Cursor usage limits made it the wrong fit at the time. That experience is not a test of every current plan or review mode. ## How Should You Write Rules for an Agent Reviewer? Write agent review rules that name the defect to flag, the files affected and the expected behaviour. General prompts tend to produce general comments. Write rules that still make sense after a refactor, and say what would prove a failure. - Tenant boundary: Flag any database query that reads customer data without an organisation filter. Shared reference tables are the stated exception. - Migration safety: Treat a destructive schema change as blocking unless the old and new application versions can run during rollout. - Payment retry: Require an idempotency key for every operation that can charge or refund money. - Review evidence: Cite the source path and call chain before claiming a cross-file break. Codex looks for a `## Code Review Rules` section in the nearest `AGENTS.md`. Claude managed review gives `REVIEW.md` the highest priority in its review pipeline. Copilot reads several repository and path-scoped instruction formats from the pull request's head branch. Keep format checks out of these files. A linter can prove spacing, types, or import order at lower cost and with the same result each run. Ask the model to check business rules, compatibility, how data moves through the code and what happens when something fails. ## When Should You Use a Second AI Reviewer? Use a second AI reviewer when an important change needs a different kind of check and the additional findings justify the review time. I use Claude Code and Codex side by side in project work, and Codex reviews sit in the promotion flow. Independent passes can expose different assumptions because the models, context systems, and prompts differ. Have a clear reason for the second review. Authorisation changes, payment retries, data migrations, and tenant boundaries can justify it. Copy edits and small dependency-free UI changes rarely need two probabilistic reviews. Ask each reviewer a different, specific question. One can inspect the requirement and regression risk, while the other traces permissions, error paths, and data loss. Sending the same broad prompt twice can leave you checking duplicate comments without finding more bugs. Compare findings only after each reviewer has supplied verifiable evidence. “Two tools agreed” can still mean both inferred the same false contract from a misleading name. A test, source citation, or reproducible path turns a comment into evidence. ## What Does Agent Code Review Cost? Agent code review costs depend on the plan, review mode, model usage and number of runs. Automatic reviews after every push can multiply the bill, even when each run is small. Claude managed review, for example, charges token-based usage rather than a fixed price per review. Codex and Copilot draw from pools also used by other agent work. OpenAI's rate card says Codex, ChatGPT Work, and other workspace agents can share agentic credits. GitHub says Copilot review shares AI credits and also uses Actions minutes for its agentic work. BugBot uses run-based usage, while local agent review may consume an existing plan allowance. The practical cost model needs four inputs: - Pull requests opened each month - Average pushes before merge - Share of changes selected for agent review - Developer minutes spent checking each comment Request reviews manually first so you can see the cost before automatic runs multiply it. Add more reviews once you know how many findings are useful and how much allowance they consume. ## What Permissions Does an Agent Reviewer Need? A read-only reviewer needs enough access to inspect the change and relevant code. A fix agent needs branch write access as well. Split those actions so a useful finding does not silently become permission to alter a protected branch. For GitHub Actions, pin actions to reviewed commit SHAs, minimise token permissions, prevent untrusted pull-request text from becoming unchecked instructions, and keep secrets out of forks. For managed Apps, inspect repository scope, retention terms, logs, and organisation controls. Assign a human owner to the review workflow. That person decides which rules belong in the repository, which findings count as false positives, when automatic triggers pause, and how spend is reviewed. An unowned bot becomes background noise even when its model is good. Keep deterministic checks in place as required and repeatable gates. Tests, type checks, static analysis, policy checks, and branch protections can fail a build in repeatable ways. Agent comments add a judgement layer before a developer gives approval. ## Treat Review Output as Untrusted Input Repository text can try to steer an agent away from its task. Comments, test fixtures, generated files, issue bodies, and pull-request descriptions may contain instructions that conflict with the review prompt. A privileged workflow should treat those sources as data and keep its governing rules outside attacker-controlled content. Review findings also arrive as untrusted claims about the code. Require a file and line, a reachable execution path, the expected behaviour, and the likely impact before changing code. When the agent calls a tool, preserve the relevant command and result so a developer can distinguish observed evidence from model inference. These controls become more important when a review can launch a fix task. The review identity should stay read-only, while a separate developer-approved job receives the confirmed finding and the minimum permission needed to edit. That boundary makes accidental or adversarial instructions less likely to turn into repository changes. ## How Should You Test an Agent Reviewer? Test an agent reviewer on representative pull requests with known defects, then record verified findings, incorrect comments, developer time and spend. The [four-week review trial](/ai-code-review/#roll-out-one-reviewer) gives you a common method. Also send the same high-risk diff to two model families with different, specific questions. Compare only findings that survive verification. Record the review mode beside each result. A local pass, managed review, and GitHub Action carry different context, permissions, and costs even when they use the same product name. Decide which kinds of changes should receive a second review from a different model family. ## Frequently Asked Questions ### Can coding agents review uncommitted code? Coding agents such as Codex and Claude Code can review uncommitted changes before you open a pull request. Local review describes where you start the task, not necessarily where the model processes your code. A local client can still send context to a hosted model. ### Why do two coding agents find different bugs in the same change? Two coding agents can find different bugs because they use different models, instructions and retrieved context. Even repeated reviews by one agent can vary. Verify each finding against the code; agreement between agents is not proof that a claim is correct. ## A Verdict on Agent Review Coding agents belong on selected reviews where their repository tools and model diversity can expose a costly blind spot. Use a dedicated reviewer for routine checks when every pull request needs a review. For the agent workflow, use read-only permissions first and require a separate approval before any finding becomes a patch. --- ## AI Code Review Tool Comparisons URL: /ai-code-review/comparisons/ Cluster: AI Code Review > Overview & AI Code Review Already deciding between two AI code reviewers? These comparisons explain where each tool fits, what it costs and how much control your team gets over its comments. If you still need a shortlist, start with my [guide to the best AI code review tools](/ai-code-review/). A longer feature list does not necessarily mean a better reviewer. The useful question is whether a tool catches problems your team would otherwise miss, without creating more work than it saves. ## Which AI Code Review Tools Should You Compare? Compare CodeRabbit with Bugbot or Greptile when choosing a dedicated reviewer, and with GitHub Copilot when deciding whether a separate subscription earns its keep. Compare Codex with Claude Code when you want review alongside a coding agent. Use the table to find the comparison you need. | Your Question | Start Here | | Dedicated reviewer or Copilot? | [CodeRabbit vs GitHub Copilot](/ai-code-review/comparisons/coderabbit-vs-github-copilot/) | | CodeRabbit or Cursor's review and repair workflow? | [CodeRabbit vs Bugbot](/ai-code-review/comparisons/coderabbit-vs-bugbot/) | | CodeRabbit or Greptile's repository context? | [CodeRabbit vs Greptile](/ai-code-review/comparisons/coderabbit-vs-greptile/) | | Codex or Claude Code for reviewing changes? | [Codex vs Claude Code](/ai-code-review/comparisons/codex-vs-claude-code/) | | CodeRabbit or Qodo for shared review rules? | [CodeRabbit vs Qodo](/ai-code-review/comparisons/coderabbit-vs-qodo/) | ## AI Code Review Comparisons Compare what each tool can read, how you configure it, what it costs and where it falls short. Local reviews and managed pull-request reviews can behave differently, even within the same product. ### Dedicated Reviewers and GitHub Copilot ### Codex and Claude Code Comparisons ### CodeRabbit and Qodo ## How Should You Compare AI Code Review Tools? Compare AI code review tools on the same representative changes, then judge their confirmed findings, missed issues, review time and total cost. First check that both support your Git host and meet your organisation's source-code access requirements. Include ordinary maintenance as well as a cross-file change and a security-sensitive change. A bot that spots one planted bug can still be exhausting on everyday work. Record the review mode and configuration so you know what you actually compared. - Confirmed defect: a reproducible problem with a clear consequence. - Useful investigation: a comment that helped a person find or understand a problem, even if the proposed fix was wrong. - False alarm or duplicate: a comment that consumed attention without revealing a new problem. - Miss: a relevant issue found by a person or another check but overlooked by the reviewer. Keep those labels consistent across both tools. Measure the time spent checking comments as well as the number of comments produced. Separate a bot's suggested fix from evidence that the fix works. Include seats, review credits, reruns, model usage and any runner charges in the full bill. The [AI code review guide](/ai-code-review/) explains the charging models and provides a fuller evaluation process. ## Frequently Asked Questions ### Should a team run two AI code review bots? A team should run two AI code review bots only when the second reviewer finds useful issues that the first misses. Give each bot a clear role and count duplicate comments as a cost, alongside subscriptions and the time people spend checking findings. ### How long should an AI code review trial run? An AI code review trial should cover enough ordinary pull requests to reveal useful findings, missed bugs and repeated false alarms. Two to four weeks is a practical starting point, rather than a guaranteed minimum; a quiet repository may need longer to produce a meaningful sample. --- ## Complete GitHub Copilot Code Review Guide (2026) URL: /ai-code-review/github-copilot/ Cluster: AI Code Review > Overview & AI Code Review GitHub Copilot code review is an automated reviewer built into GitHub and supported editors. It reads pull requests or local edits, posts findings, suggests fixes, and can hand work to Copilot cloud agent. Teams can request it by hand or apply it through GitHub rulesets. The product has moved beyond its early diff-only form. Current review can gather full project context, run setup steps, use repository instructions, work behind a configurable firewall, and spend GitHub Actions minutes as well as AI Credits. Copilot is worth trying first if your team already uses GitHub and a paid Copilot plan. It will not suit every repository. Our own team removed mandatory Copilot reviews after finding them too noisy. Compare other [AI code review tools](/ai-code-review/) if the same problem persists for you. ## Copilot Code Review at a Glance Some Copilot review features are still in public preview and may change. | Area | Current Position | Team Check | | Review result | Comment by default; approvals in preview | Who owns bot approvals | | Manual trigger | Request Copilot reviewer | Under 30 seconds typical | | Automatic trigger | GitHub ruleset | Review-on-push noise | | Context | Full project gathering | Runner path works | | Review effort | Lite or Balanced | Balanced costs more | | Usage cost | AI Credits and Actions | Budgets set first | ## What Can GitHub Copilot Code Review Do? GitHub Copilot code review checks proposed changes for potential bugs and improvements, then posts comments and suggested fixes. GitHub says it supports code in any language. It examines a pull request from several angles, leaves inline findings, and often attaches a suggested edit. A developer can apply one suggestion, group suggestions into a commit, or ask Copilot cloud agent to prepare a fix. The feature appears on GitHub.com, GitHub Mobile, GitHub CLI, VS Code, Visual Studio, Xcode, and JetBrains editors. Azure DevOps is in public preview. An organisation may need to enable the code review policy before members can use it. GitHub also lets eligible organisation members without a Copilot licence request review on GitHub.com. An admin must allow paid AI Credit use and turn on the unlicensed-member policy. Those reviews can become direct organisation usage instead of drawing from a personal allowance. Copilot code review remains an adviser in the merge flow by default. GitHub tells teams to validate its feedback and add human review. A September 2026 preview lets admins allow Copilot approvals, so write down when those approvals may count and enforce that policy through branch rules. ## How Do You Request a Copilot Code Review? Request a GitHub Copilot code review by opening the pull request’s Reviewers menu and selecting Copilot. GitHub says the review usually returns in under 30 seconds. By default it appears as a Comment review with inline items that developers can reply to, resolve, hide, or rate. Replies remain visible to people, yet Copilot does not read or answer them in that thread. Use the reaction controls to send product feedback. Keep technical debate in the pull request where the human reviewers can resolve it. Automation can request the reviewer through GitHub’s REST API using `copilot-pull-request-reviewer[bot]`. The REST API request is the documented mechanism; there is no magic comment form of it, so do not expect a comment like `@copilot review` to do the same job. Test the request on a sandbox repository before adding it to a workflow. Request reviews manually for the first month so you can track their cost and usefulness. Ask for Copilot on a mix of normal work, known-bug examples, and PRs where repository context should matter. Record findings before enabling an organisation-wide rule. ## Can Copilot Review Code Before You Open a Pull Request? Copilot can review local edits before they become a pull request. In VS Code, a developer can run review against uncommitted changes and see findings inline or in the Problems panel. Applied suggestions alter the working tree and are not committed automatically. This pre-push pass serves a different purpose from shared review. It helps an author catch a local mistake before asking teammates, while the GitHub review creates a record on the pull request. Teams can use both without treating either as approval. GitHub also lists review in Visual Studio, Xcode, JetBrains editors, GitHub CLI, and GitHub Mobile. Availability and controls differ by interface, so use the matching official tab when documenting a team workflow. The Free plan includes neither pull request reviews nor file diff reviews in editors, according to GitHub's plan comparison. Azure DevOps review is in public preview, which means rollout details can move. Test repository policy, billing attribution, comments, and runner behaviour in Azure DevOps before treating it like the GitHub.com path. ## How Much Does GitHub Copilot Code Review Cost? GitHub Copilot code review consumes AI Credits on eligible plans, with Actions usage added for agentic work. Paid individual plans start at $10 per month; organisation billing and allowances depend on the plan and policy. The subscription price is not an unlimited-review price. | Plan | Price | Review | Buyer | | Free | $0 | Not included | Individual trial | | Pro | $10/user/mo | Included with credits | Individual developer | | Pro+ | $39/user/mo | Included with credits | Higher individual use | | Max | $100/user/mo | Included with credits | Highest individual allowance | | Business | $19/user/mo | Policy and budgets | Organisations | | Enterprise | $39/user/mo | Enterprise controls | Large organisations | The listed plan price only forms the base. Each pull-request or editor review consumes AI Credits based on the model and tokens processed. Full project context and other agentic functions also use GitHub Actions runner minutes. GitHub's billing guide says each plan has a monthly AI Credit allowance. Paid extra use, budgets, and existing annual terms can alter the bill. Check the account's live billing page before forecasting automatic review. Cost per useful finding is a better measure than reviews per month. Divide AI Credit and runner spend by comments that led to a verified fix. Include the time spent dismissing repeats and checking generated edits. ## How Do You Enable Automatic Copilot Reviews? Enable automatic Copilot review through a repository or organisation ruleset that targets the pull requests you want reviewed. The automatic review guide also offers review on draft pull requests and each new push. Enable review for open pull requests on a few repositories first. Draft review can send feedback while code is still moving, and review on every push can repeat work. GitHub’s enterprise guidance warns that the extra triggers can create more noise. Rulesets can target branches and repository name patterns. Organisation owners can apply one policy across selected repositories, while repository owners can define a local rule. Exclusions apply after inclusion patterns, so check the final target set before activation. Who pays depends on how the review starts. For an automatic review, GitHub attributes AI Credits to the pull-request author; a manual request by another person attributes usage to that requester. Copilot cloud-agent pull requests are billed first to the human co-author, falling back to the organisation. Reviews of other bot-authored pull requests, or reviews requested by other bots, are billed directly to the organisation. Administrators can also choose organisation billing for licensed members. An exhausted budget can block later review runs. On Business and Enterprise plans, user, enterprise, or cost-centre limits govern code-review access. Test the blocked state so authors know whether a missing review means clean code, exhausted budget, or a failed runner. ## How Do Copilot’s Lite and Balanced Reviews Differ? Copilot’s Lite review favours faster, lower-cost feedback, while Balanced uses more reasoning for complex changes and is the built-in default. Both can use GitHub Actions runners to gather project context. GitHub says this makes reviews more specific because the service can inspect the repository around the pull request, including code beyond the touched lines. The context path depends on runner availability. If GitHub Actions is unavailable or a review workflow fails, Copilot still produces a review with fewer agentic functions. Larger hosted runners cost more per minute, while self-hosted runners avoid GitHub Actions minute charges but leave runtime operation with the team. GitHub estimates $0.05 to $1 in AI Credits for a Lite review and $0.25 to $5 for Balanced, before applicable Actions usage. These are estimates, not fixed per-review charges. Balanced can consume more AI Credits and Actions minutes. Use it on repositories where a missed contract or security bug costs enough to warrant that spend. Routine documentation or small local edits can remain on Lite. Test repository context with a controlled contract break. Rename an exported function without editing one caller, alter a shared schema, or remove an authorisation check used by another package. A valid finding should name the related path and explain the contract. ## How Do You Customise Copilot Code Review? Customise Copilot code review with repository instructions, path-specific rules and, where needed, review setup steps or external context tools. GitHub currently names `.github/copilot-instructions.md`, path-scoped `*.instructions.md` files, `AGENTS.md`, agent skills, `REVIEW.md`, `GEMINI.md`, and `CLAUDE.md`. A July 17, 2026 update moved review instructions to the pull request's head branch. That lets a team test a new rule before merging it. Treat instruction edits as code because an untrusted branch can alter the reviewer's guidance. Review-specific setup uses `.github/workflows/copilot-code-review.yml` in the repository. The workflow can install dependencies and tools or prepare the environment. When that file is absent, code review can fall back to an existing `copilot-setup-steps.yml`. A precise review instruction might read: “For changes under `src/billing/`, require a test for signature failure and reject logging of the raw webhook body.” The condition names a path, a failure case, and forbidden behaviour. Broad praise or “review carefully” adds little. The July update also put code review behind a firewall by default on eligible hosted runners. Admins can configure its internet access apart from Copilot cloud agent. Self-hosted review runners do not get that firewall, so the team must set its own network boundary. Agent skills and Model Context Protocol servers can provide review context. GitHub says review is more likely to use them when names and instructions point clearly to relevant issue or incident data. Keep access narrow and verify which external data appeared in the review. ## Can Copilot Approve Pull Requests or Block Merges? Copilot can approve a pull request only when the approval preview is enabled under the required policies. By default it submits comments, and its findings do not block a merge. An approval assessment in a summary is not itself an approving review. Some files are excluded from code review. GitHub’s current list includes dependency management files such as `package.json` and `Gemfile.lock`, log files, and SVG files. Teams that rely on lockfile, manifest, or generated-asset review need separate checks. By default Copilot returns a non-blocking Comment review status. It does not request changes or block merging. Approvals that count toward the required-approval rule exist only in a public preview that admins switch on, with optional file-path limits, and GitHub dismisses that approval when new commits arrive. Keep human approval and deterministic gates such as tests, type checks, static analysis, and security scans. Every suggested fix still needs careful human inspection. Check the diff, run the relevant test, and confirm the edit preserves behaviour outside the reported line. Sending a suggestion to cloud agent creates more code to review; it does not prove that the finding is settled. Track misses as carefully as accepted comments. If a human finds a severe bug after Copilot ran, add it to the trial set. Replaying known misses shows whether new instructions, Balanced effort, or a product update altered the result. ## What We Changed in Our Review Process We removed mandatory Copilot review from one of our repositories on July 19, 2026. The comments were advisory and noisy in that workflow, so Codex became the primary release reviewer and CodeRabbit remained optional. That was one repository. Repository mix, instructions, effort level, and product updates can lead to another result. That experience showed why someone needs to take responsibility for the reviewer. An automatic reviewer needs someone to measure accepted findings, repeated comments, misses, and cost. Do not keep a noisy reviewer mandatory just because you already pay for Copilot. ## Test Effort and Repository Context Use the [four-week review trial](/ai-code-review/#roll-out-one-reviewer) to measure verified findings, incorrect comments, developer time and spend. For Copilot, compare Lite with Balanced on the same kind of change and check whether repository instructions were actually used. Also inspect a run where setup fails. Copilot can continue with reduced context, so a completed review does not prove that all context tools worked. ## When Should You Choose a Dedicated AI Reviewer? Choose a dedicated AI reviewer when Copilot’s feedback, file coverage, hosting options or reporting do not meet your needs. Copilot remains convenient for teams already using GitHub, so test the limitation before adding another service. A dedicated reviewer may justify its bill if you need different controls, reports, Git hosts or review settings. Compare confirmed findings and total review cost with [CodeRabbit](/ai-code-review/coderabbit/), [Qodo](/ai-code-review/qodo/), or [Graphite Agent](/ai-code-review/graphite/) on the same pull requests. Another tool does not remove the human gate. Two bots can repeat the same wrong claim and create twice the reading. Give each reviewer a purpose, such as fast baseline coverage or security-sensitive analysis, then remove overlap that does not lead to fixes. Use the broader [GitHub Copilot guide](/ai-coding-tools/copilot/) to assess the rest of the product. Copilot can still earn a seat through completions, chat, agents, or CLI work even when its pull-request reviewer does not fit the repository. ## My Verdict GitHub Copilot code review should get the first measured trial when a team already pays for Copilot and keeps its pull requests on GitHub. Manual requests, current instructions, Lite effort, and a small known-bug set provide enough evidence without adding a vendor. Keep it when confirmed findings justify AI Credit use, runner minutes, and developer attention. Remove the automatic rule or bring in a dedicated reviewer when noise, misses, excluded files, or review ownership outweigh GitHub-native convenience. Keep human approval required unless you have named an owner for bot-approved merges. ## Frequently Asked Questions ### Which model does Copilot code review use? GitHub Copilot code review uses a GitHub-managed combination of models, prompts and review tools. You cannot select its model through the Copilot Chat model picker, and organisation Chat model settings do not control code review. ### Can Copilot review files beyond the diff? Copilot can inspect repository files beyond the pull-request diff to understand related code. That context depends on the available review tools and runner setup. If setup fails, Copilot may still return a review with reduced context. ### Does Copilot answer replies to its review comments? Copilot does not read or answer replies in its code-review comment threads. Those replies remain useful to human reviewers. Use the documented review controls to request another pass, or a separate cloud-agent task to implement a confirmed fix. --- ## Complete Graphite Agent Review (2026) URL: /ai-code-review/graphite/ Cluster: AI Code Review > Overview & AI Code Review Graphite Agent is the AI reviewer inside Graphite’s GitHub pull-request platform. It comments on bugs, suggests fixes, and can carry a conversation about a finding. The same product also sells stacked pull requests, a review inbox, automation, and a merge queue. Graphite makes most sense when a GitHub team wants smaller pull requests and a better review workflow as well as AI feedback. If you only need a bot to spot bugs, compare its per-seat cost with the other [AI code review tools](/ai-code-review/) before moving your team into another interface. ## Graphite Agent at a Glance Graphite Agent is available on paid plans and the free personal tier. It connects to GitHub. | Area | Current Position | Trial Question | | Git platform | GitHub | Repos sync cleanly? | | Review trigger | New pull requests | Useful on routine work? | | Context | Whole codebase claim | Finds cross-file bugs? | | Tuning | Rules and exclusions | Noise falls over time? | | Workflow | Stacks and merge queue | Team wants both? | | Paid model | Per seat | Seat rules understood? | ## What Does Graphite Offer Beyond AI Review? Graphite combines AI review with tools for managing small, dependent pull requests. Its product includes a CLI and editor extension for stacks, a web inbox, merge controls, team reporting, and GitHub sync. Agent is part of that wider review system. This matters to buyers because the Team price covers unlimited Agent use alongside the wider review platform. If a team already wants stacking and a merge queue, the AI reviewer is one part of a larger purchase. If it wants only automated comments, the wider workflow takes time to learn. Graphite says Agent can create PR titles and descriptions, review code, chat about findings, propose fixes, and summarise continuous-integration results on eligible plans. Custom rules, file exclusions, automations, and acceptance metrics add a feedback loop around those comments. Start with what slows your team down. Slow handoffs may call for the inbox and stack workflow, while missed bugs may mean you need better review rules, more context or another reviewer. Merge delays may call for queue rules. The bundle only pays off when the team uses those features. ## How Does Graphite Agent Review Code? Graphite Agent analyses pull requests in repositories selected by an admin, leaves inline comments on potential problems and can suggest fixes. The review dashboard reports pull requests reviewed, issues found, and acceptance rates. Graphite's current product page says Agent analyses the entire codebase, learns from team feedback, and focuses on bugs instead of style. These are vendor claims that need a repository-specific trial. Agent works alongside the continuous integration pipeline. It may find logic and edge cases that tests miss, while tests provide repeatable proof. Keep required checks and human approvals in place because a conversational reviewer cannot guarantee full coverage. ## How Do Stacked Pull Requests Change Code Review? Stacked pull requests divide a larger change into smaller, dependent reviews. Developers can ask for review on the first slice while building the next. Graphite tracks the chain and helps move later branches as earlier ones merge. Smaller diffs can help both people and AI because each pull request carries a tighter purpose. The reviewer sees fewer unrelated edits, comments attach to a clearer unit, and authors can fix one slice without reopening a large change. Stacks also alter the team’s daily Git work. Developers need the Graphite CLI or editor flow, must understand parent-child branches, and have to recover when a middle pull request changes. A mixed team, where only some authors stack, can create more coordination work than it removes. Graphite Agent can run without stacked pull requests. The setup page says selected repositories receive automatic review on new pull requests. Trial that basic loop first if the team has not agreed to alter how it divides work. ## How Much Does Graphite Cost? Graphite's billing table lists Hobby for personal work, Starter at $20 per seat each month billed annually, Team at $40 per seat each month billed annually, and custom Enterprise terms. | Plan | Price | Access | Buyer | | Hobby | Free | Limited reviews and chat | Personal repositories | | Starter | $20/seat/mo (annual) | Limited reviews and chat | Small teams | | Team | $40/seat/mo (annual) | Unlimited and tunable | Growing teams | | Enterprise | Custom | Unlimited and governed | Large organisations | The Team plan adds review customisation, suggested fixes, CI summaries, code-indexing controls, privacy controls, automation, and a basic merge queue. Enterprise adds items such as SAML or SSO, audit logs, access controls, GitHub Enterprise Server, and private uploads. Check who counts as a paid seat. Its pricing FAQ says anyone who joins the workspace or has a pull request reviewed by Agent counts as a team member. Ask Graphite how that rule applies to bots, contractors, occasional authors, and archived accounts. The 30-day Team trial allows unlimited invited contributors, according to the billing docs. Use it to build a full seat list and a monthly cost before the trial ends. A five-person Team workspace lists at $200 each month on annual billing, while a larger author pool may alter that figure. ## How Do You Set Up Graphite AI Reviews? To set up Graphite AI reviews, an admin connects the GitHub App, syncs the required repositories and enables them in AI review settings. Agent reviews new and updated pull requests that match the settings. You can tune Graphite with comment exclusions and custom rules. Exclusions stop known unhelpful comments, while rules state a practice the reviewer should enforce. The customisation guide recommends narrow prompts with clear examples. A workable exclusion might say: “Skip performance comments for files under `/scripts` because each script runs once during migration.” A workable rule might require a public API field to appear in the schema and contract test. Both examples define a clear scope and evidence. Generated files can be marked with `linguist-generated=true` in `.gitattributes`. Graphite says those files collapse in review, do not count toward a too-large PR check, and are skipped by Agent. Test this with your generated paths before relying on it. ## How Can You Judge Graphite’s Review Quality? Judge Graphite’s review quality by the valid bugs it finds, the time spent dismissing incorrect comments and the bugs it misses. Its customisation dashboard provides supporting metrics for each rule and exclusion. Rules report issues found, PRs reviewed, accepted issues, acceptance rate, and votes. Exclusions report the issues checked and filtered out. Acceptance rate is a starting point, not a quality score. A developer may accept a small edit because it is cheap, while ignoring a more serious finding that needs design work. Pair the number with severity, fix size, later test results, and bugs found after merge. Review each custom rule after enough pull requests to expose a pattern. A rule with many findings and few accepted fixes may be vague, wrong for part of the repository, or better handled by a deterministic check. Narrow it, split its scope, or remove it. Exclusion data can expose overly broad review tuning. If one exclusion filters a large share of candidate findings, sample those hidden cases. A broad instruction that cuts noise can also conceal a valid performance or security issue outside the reason it was written. Keep a list of bugs found by people or tests alongside Graphite’s dashboard. Check a cross-file change and a dependent stack, then compare the findings with a dedicated reviewer. The [AI reviewer evaluation guide](/ai-code-review/#roll-out-one-reviewer) explains how to keep that comparison fair. ## How Does Graphite Handle Repository Access? Graphite accesses selected repositories through its GitHub App; code-indexing and AI privacy controls are listed on Team and Enterprise. Enterprise adds access controls, SAML or SSO, audit-log export, GitHub Enterprise Server, private uploads, and contract options. Exact settings should be shown in the plan you intend to buy. Repository indexing raises data questions. Ask what Graphite indexes, where that index is stored, how soon deleted repositories leave it, which model providers receive code, and whether chat or suggested fixes create extra stored data. The GitHub App permission set needs a repository-by-repository check. Confirm who can sync a new repository, enable automatic reviews, edit rules, view organisation metrics, and export audit records. Start with the fewest permissions and repositories needed for the trial. Add access only when the work requires it. ## How Does Graphite Connect to Cursor? Graphite connects to Cursor Cloud Agents so developers can request code changes from the pull-request page. A developer signs in with a paid Cursor account, authorises the GitHub organisation and grants access to the repository. The agent can create a draft pull request or update the author's existing branch. Graphite supports continuing a Cursor Cloud Agent conversation inside the pull-request page. This is a shipped workflow, rather than only an acquisition roadmap. The integration requires Cursor Pro or above; do not assume Graphite's included AI reviews also cover coding-agent usage. Graphite Agent and [Cursor Bugbot](/ai-code-review/bugbot/) have separate product and pricing pages. A paid Cursor connection for coding tasks does not mean Bugbot is included in a Graphite subscription. ## What Are Graphite’s Main Limitations? Graphite’s main limitations are its GitHub focus, per-seat costs and the work of adopting its wider review workflow. Graphite’s current public setup relies on its GitHub App. Teams on GitLab, Bitbucket, or Azure DevOps need another review route unless Graphite publishes a new integration. Per-seat billing can spread beyond the small group that administers Graphite. Anyone whose pull request Agent reviews may count as a member. Teams with many occasional authors should settle the roster rules before enabling every repository. ## My Verdict Graphite Agent has the clearest case when a GitHub team also wants Graphite’s stack, inbox, automation, or merge-queue workflow. In that setting, unlimited Team-plan review joins a platform the team has another reason to use. Run Agent alone on ordinary pull requests before changing Git habits. Adopt the wider platform if it catches useful bugs and saves enough review time to justify the price per person. If you only need a review bot, compare those results and costs with a dedicated reviewer. ## Frequently Asked Questions ### Is Diamond now Graphite Agent? Graphite renamed Diamond and combined it with Graphite Chat under the Graphite Agent name in October 2025. Older Diamond reviews may describe prices, limits or screens that no longer apply. ### Can I use Graphite Agent without stacked PRs? Graphite Agent can review ordinary GitHub pull requests without requiring stacked PRs. An admin enables AI reviews for selected repositories, so a team can try the reviewer before changing how developers organise their branches. --- ## Complete Greptile Review (2026) URL: /ai-code-review/greptile/ Cluster: AI Code Review > Overview & AI Code Review Greptile is an AI code reviewer that uses a graph of your repository to follow connections between files, functions and dependencies. It is most relevant when a small change can break code elsewhere, such as a shared API or a library used by several services. I would shortlist Greptile for a team struggling with cross-file bugs. For a small, well-tested repository, the extra context may add less value. I have not run a new hands-on Greptile test. Our [best AI code review tools](/ai-code-review/) guide separates the tools we run from those we assess through published information. ## Greptile at a Glance Before subscribing to Greptile, check the current plan limits and whether it supports your Git provider. | Area | Current Position | Buyer Check | | Review method | Repository graph plus diff | Cross-file findings | | Providers | GitHub and GitLab apps | Your exact provider | | Free entry | Starter, one developer | Active-developer limit | | Paid price | US$30 per seat monthly | Seats each billing period | | Usage meter | Credits per review | Review depth and reruns | | Enterprise | Self-hosting and compliance | Contract details | ## How Does Greptile Review Your Code? Greptile reviews pull requests using an index of the repository's files, functions and dependencies. On a pull request, the product uses that graph to review the change with repository context and posts comments with suggested fixes. Its overview says typical findings arrive in about three minutes. Test a change that affects code in another file. Imagine a handler changes the shape of a result, while a background job and a client library consume that result elsewhere. A diff-only reviewer may only see the handler. Greptile also says reactions and replies teach it what a team cares about, with less unwanted feedback after two to three weeks. That is a vendor product claim, so validate the behaviour on your own repository. ## Who Should Use Greptile? Greptile best suits teams reviewing changes across shared libraries, services or packages, where the affected code is hard to see in one diff. A small repository with clear tests may still benefit from a second reviewer, but there may be fewer hidden dependencies to find. Try it on changes where understanding related files could reveal a missed bug. ## How Much Does Greptile Cost? Greptile's pricing page lists a free Starter plan for one active developer with 50 monthly credits, and a Pro plan at US$30 per seat per month with 50 credits per seat. A Base review uses one credit, Plus uses three and Apex uses ten. Additional credits cost US$1 each, so deeper reviews can use the included allowance much faster. Greptile's published plans. Confirm before any purchase. | Plan | Price | Included Credits | | Starter | Free, one active developer | 50 each month | | Pro | US$30 per seat monthly | 50 per seat | | Extra credits | US$1 each | Bought as needed | | Enterprise | Custom pricing | Negotiated | The same page lists a 14-day trial for Pro, unlimited repositories, custom rules, and external-app connections. It presents Enterprise as custom pricing with self-hosting and compliance options. Prices, credit rules, discounts, and plan features are volatile, so confirm them on the official page before a purchase or a budget approval. Greptile's changelog says a free individual Starter tier launched June 29, 2026. The pricing page also says qualified non-commercial projects with an OSI-approved open-source licence can apply for free service and eligible early-stage startups can apply for a discount. ## How Should You Test Greptile? Test Greptile on one repository using known cross-file defects and compatible changes that should produce no warning. The [AI reviewer trial method](/ai-code-review/#roll-out-one-reviewer) explains how to record the results. Choose a pull request where a changed type, schema, or authorisation helper has a known consumer outside the diff. 1. Name the changed symbol and the dependent path before the review runs. 2. Keep the known contract failure hidden from the review prompt. 3. Check whether Greptile identifies the dependent and explains the broken behaviour. 4. Repeat with a compatible change that should produce no finding. 5. Record which bugs required reading related files and which were visible in the changed lines alone. ## How Does Greptile Learn from Feedback? Greptile learns team preferences from reactions and replies to review comments. Consistent feedback helps it distinguish a useful finding from a suggestion your team does not want. For a Greptile comment, establish whether the behaviour is reachable, add a test or inspection note that explains the decision, and react consistently. A confirmed defect gets fixed with reproducible evidence. A false positive gets a concise, evidence-backed dismissal. Assign an owner to useful work you defer rather than leaving it in an unresolved thread. Greptile's documentation describes thumbs-up and thumbs-down reactions plus replies as inputs to its learning loop. That creates a reason to avoid casual reactions. If different reviewers signal different priorities, the tool may learn conflicting preferences. Agree on a few categories first: correctness, security, contract compatibility, test coverage, or out of scope. | Comment Result | Reviewer Action | Evidence | | Confirmed defect | Fix and react | Failing path or test | | False positive | Dismiss and explain | Guard or contract | | Deferred concern | Create owned work | Scope decision | ## How Do You Verify Greptile Findings? Verify a Greptile finding by tracing the changed code to the affected caller and checking whether the reported failure can occur. Look for a comment that names the changed function or value, the code that uses it, and what would break. Then follow that named path through the repository. If the comment cannot identify the caller, contract, or state transition involved, treat it as a question for a reviewer. It is not yet a defect ready for repair. Use a small change set for the first tests. A renamed field that is consumed by a serialiser, a changed error type handled in another package, or an authorisation helper used by several routes can all show whether the repository graph adds useful context. Include the existing relevant tests in the inspection. A true cross-file issue should either change a test result or expose why no test covers the affected contract. - Trace the comment from the edited line to every named dependent. - Check whether a type, validator, or adapter preserves compatibility. - Confirm the affected consumer can receive the changed value at runtime. - Add a regression test that crosses the same boundary. ## How Do You Configure Greptile? Configure Greptile with a `.greptile/` folder containing review settings, plain-language rules and references to relevant project files. Place the folder at the repository root or inside a directory. Settings cascade from root to leaf, while rules and file references accumulate across the matching folders. The recommended folder supports three optional configuration files. `config.json` holds documented review settings and structured rules. `rules.md` holds plain-language instructions scoped to that directory. `files.json` points Greptile at existing schemas, API specifications, or architecture documents. ``` {`.greptile/ ├── config.json ├── rules.md └── files.json // .greptile/config.json { "strictness": 2, "rules": [ { "id": "tenant-writes", "rule": "Trace authenticated tenant ownership to every data write.", "severity": "high" } ] } // .greptile/rules.md ## Contract changes Flag a changed public response shape when no contract test covers it. // .greptile/files.json { "files": [ { "path": "docs/architecture.md", "description": "Service boundaries and ownership" } ] }`} ``` A nested folder can override scalar settings, disable an inherited structured rule by its ID, and add local rules or context files. [Keep the current `.greptile/` reference open while editing these files.](https://www.greptile.com/docs/code-review/greptile-config) Greptile still supports `greptile.json` for backward compatibility, but `.greptile/` takes precedence when both exist in one directory. Local configuration is useful when the repository has meaningful boundaries. An API directory can ask for authorisation tracing, while a payment module can ask for idempotency and error-path review. Write rules that describe behaviour you can check, rather than copying your whole engineering handbook into the prompt. ## Can Greptile Hand Fixes to Coding Agents? Greptile can use Fix in your IDE on a review comment to send the file, line numbers, review comment, and suggested fix to Claude Code, OpenAI Codex, Conductor, Cursor, or Devin. It also documents a separate Fix All action. An agent handoff is a convenience for a confirmed finding, not proof that the proposed patch is correct. Read the complete review finding before assigning the repair. Inspect the generated diff against the confirmed failure path. Run the relevant test suite and project checks. Then request a fresh review only if the repair changed the path that prompted the comment. This sequence keeps the human reviewer responsible for the code that merges. ## What Are Greptile's Main Limitations? Greptile can still miss dependencies, misunderstand project requirements and produce false positives. Its repository graph gives the reviewer more context; it does not prove a finding is correct. Hosted plans centre on GitHub and GitLab, while Enterprise lists additional self-hosted providers and deployment options. Finally, settle the cost question from your own pull-request volume. Credits show the charge for a run. The full cost also includes checking comments, making fixes and rerunning reviews. The [CodeRabbit guide](/ai-code-review/coderabbit/) and [BugBot guide](/ai-code-review/bugbot/) show other review workflows to test against the same rubric. ## The Case for a Greptile Trial Judge Greptile by the bugs it catches across related files. Give it contract breaks that ordinary diff review misses, require every comment to identify the dependent path, and use `.greptile/` to supply only the context needed for that proof. A team with mostly compact, well-tested repositories may gain little from the graph and may not need another reviewer. ## Frequently Asked Questions ### Does Greptile read the whole codebase? Greptile indexes the selected repository and retrieves related files, functions and dependencies during a review. Indexing a repository does not mean every file is included in every model request or every dependency will be understood correctly. ### Which Git providers does Greptile support? Greptile offers GitHub and GitLab integrations on its hosted plans. Enterprise also lists GitHub Enterprise Server, GitHub Enterprise Cloud with data residency, GitLab Self-Managed, Bitbucket Data Center, Gitea and on-premises Perforce/P4. Check the deployment requirements for your chosen host. ### Can Greptile send a fix to Codex? Greptile can send a review finding, file paths, line numbers and a suggested fix to Codex through its Fix in your IDE action. Codex then handles the proposed change; a developer must still check the diff and relevant tests before merging. ### Is Greptile free for open source? Qualified non-commercial projects with an OSI-approved open-source licence can apply for free Greptile access. Greptile also offers a free Starter plan for one active developer with 50 monthly credits, subject to its current plan terms. ## Keep Reading - [AI code review hub](/ai-code-review/) - [CodeRabbit guide](/ai-code-review/coderabbit/) - [BugBot guide](/ai-code-review/bugbot/) - [Greptile vs GitHub Copilot](/ai-code-review/comparisons/greptile-vs-github-copilot/) --- ## How AI Code Review Actually Works URL: /ai-code-review/how-ai-code-review-works/ Cluster: AI Code Review > Overview & AI Code Review AI code review uses a language model to examine a code change, look for defects and explain its findings. Surrounding code and repository rules help the model distinguish a real bug from a convincing guess. The [AI code review tools guide](/ai-code-review/) compares the products. A missing tenant filter can let a user read another organisation's invoice. Following that bug from the changed code to the review finding shows where an AI reviewer can catch it and where the reviewer can fail. When I judge a reviewer, I want to know whether it saw the relevant code and whether its finding reproduces, long before I ask which model it runs. Performance figures published by vendors are not independent test results. ## What Is AI Code Review? An AI code reviewer is a service that reads a proposed change, gathers some surrounding context, asks one or more language models to find problems, and posts the results where the team already reviews code. Most install as an app on GitHub, and several also support GitLab, Bitbucket, or Azure DevOps. Some also run locally before a pull request exists. A new engineer on your team needs the diff, a tour of the codebase and the team's review rules before their feedback is worth acting on. An AI reviewer needs the same three inputs, but it can still invent a convincing explanation after receiving all three. AI review differs from both static analysis and human review. A linter or SAST scanner applies fixed rules and returns the same result for the same input and configuration. A human reviewer brings judgement and knows the product. The AI reviewer uses a model to reason about the change, but its answers can vary between runs. A coding agent is the other neighbour. Codex and Claude Code write code and can also review it, while dedicated products such as [CodeRabbit](/ai-code-review/coderabbit/) and [Greptile](/ai-code-review/greptile/) are built around the pull request itself. The [guide to coding agents as reviewers](/ai-code-review/coding-agents-as-reviewers/) covers that split in depth. Useful code review terms: - Diff: the lines a pull request adds, removes, or changes, plus a few lines of surrounding context. - Context: anything beyond the diff that the model gets to read, from neighbouring files to linked tickets. - Rules file: a markdown or config file in the repository that tells the reviewer what your team cares about. - Finding: one reported problem, usually tied to a line and tagged with a severity. - False positive: a claimed defect that is not actually present, perhaps because a guard outside the reviewed context already prevents it. ## How Does an AI Code Review Work? A typical AI pull request review fetches the diff, gathers relevant context, applies review instructions and filters candidate findings before posting comments. Some products also run analysis tools or verify findings with additional agents. This example adds an invoice lookup beside an existing function that lists invoices for an organisation. ```diff --- a/billing/invoices.py +++ b/billing/invoices.py @@ -21,3 +21,9 @@ def list_invoices(org_id): "SELECT id, total FROM invoices WHERE org_id = ?", (org_id,), ).fetchall() + +def get_invoice(invoice_id, org_id): + return db.execute( + "SELECT id, org_id, total FROM invoices WHERE id = ?", + (invoice_id,), + ).fetchone() ``` That snippet is the new hire's day one. The query is parameterised, so it's safe from SQL injection, and nothing in the added lines looks broken. The bug is that `org_id` arrives as an argument and never reaches the query, so any user can read any organisation's invoice by guessing an ID. A diff-only reviewer sees the neighbouring `WHERE org_id = ?` filter just because Git includes three lines of context by default. Move the new function to another file and that clue disappears. The rest of the pipeline exists to put the clue back on purpose: 1. A trigger fires when the pull request opens, gets a push, or receives a mention such as `@codex review`. 2. The service fetches the diff and decides which changed files are in scope. 3. It gathers context from the repository, an index, linked tickets, or earlier review comments. 4. It loads your rules files and any learned preferences for this repository. 5. Some products run linters and security scanners and pass their output to the model. 6. One or more model passes produce candidate findings, and a filter or verification step drops weak ones. 7. The remaining findings become inline comments, a summary, and often a check run on the pull request. 8. Reactions and replies feed back into what the reviewer flags next time. ## What Does an AI Reviewer Need to Check Your Code? An AI reviewer needs the changed code, enough surrounding context to understand its effects, and clear instructions about repository-specific risks. The following stages explain how products gather and use those inputs, and where a missed finding can originate. ### Triggers and the Diff The trigger decides how often you pay and how much of the change gets read. GitHub Copilot, by default, reviews a pull request only when someone requests it, and users, repository owners or organisation owners can switch on automatic review. Unless it's configured to review each push, Copilot's automatic review runs once per pull request. BugBot takes a different default for repeat reviews. Cursor's documentation says it reviews only the changes since its previous review, and you can switch off Incremental Review to reread the full diff on every push. That saves money, though a bug that spans an early commit and a late one may never sit inside a single review window. Scope rules then trim the diff before any model reads it. Copilot's documentation lists file types it never reviews, including lockfiles such as `package-lock.json` and `Cargo.lock`, plus `requirements.txt` and `tsconfig.json`. A pull request that only bumps a dependency gives Copilot almost nothing to read, which is one reason dependency risk belongs with tools like [Dependabot](/appsec/dependabot/) and [Socket](/appsec/socket/). ### Context Gathering and Indexing Context is the codebase tour for the new hire, and vendors take very different routes to it. Greptile builds a graph of the whole repository when you sign up, then queries it during each review for the callers and look-alike functions elsewhere in the code. Its own docs show it comparing a changed SQL helper with sibling functions to check whether each one uses parameterised queries. CodeRabbit clones the full repository into a sandboxed cloud environment for each review and runs agents that explore it for context. Its FAQ also describes code indexing that stores vector representations of your code, using an approach called embeddings. Code chunks become numeric vectors, and the reviewer retrieves the chunks closest in meaning to the change. GitHub Copilot gathers what GitHub calls full project context through agentic tool calls that run on GitHub Actions. If Actions workflows are unavailable or fail, Copilot still posts a review, just without those agentic extras. Anthropic's managed Claude Code Review sends several agents through the diff and the surrounding code on its own infrastructure. Context also comes from outside the code. Qodo documents four sources for its review agents: the codebase and its architecture, pull request history, linked tickets and requirements, and the organisation's standards. Greptile connects to Jira and Linear, and BugBot reads existing pull request comments so it doesn't repeat what a person already said. ### Rules and Configuration Files Rules files are the new hire's team handbook. They are plain text in the repository, and the model reads them as instructions alongside its default review prompt. Each product takes rules through its own files or platform settings, with different scoping. Where each reviewer reads its rules, taken from each vendor's documentation. | Reviewer | Where Rules Live | How Scope Works | | [CodeRabbit](/ai-code-review/coderabbit/) | `.coderabbit.yaml`, plus detected guideline files | A guideline covers its own directory | | [BugBot](/ai-code-review/bugbot/) | `.cursor/BUGBOT.md` files | Root file plus files above changes | | [GitHub Copilot](/ai-code-review/github-copilot/) | `.github/copilot-instructions.md` and `AGENTS.md` | Read from the head branch | | [Greptile](/ai-code-review/greptile/) | `.greptile/` folders or `greptile.json` | Child folders override the root | | [Codex for code review](/ai-code-review/codex/) | `AGENTS.md` Code Review Rules section | Root plus the nearest file | | [Claude Code for code review](/ai-code-review/claude-code/) | `REVIEW.md` and `CLAUDE.md` | REVIEW.md reaches every review agent | | [Qodo](/ai-code-review/qodo/) | Review Standards in the Qodo platform | Learned from code and PR history | CodeRabbit's detected guideline files include `AGENTS.md`, `CLAUDE.md`, `.cursorrules`, and `.github/copilot-instructions.md`, so a rule written for your coding agent can quietly start steering your reviewer too. OpenAI's Codex documentation shows the rule shape that works best across all of them: one outcome to flag, and the safe path. ```markdown ## Code Review Rules ### Tenant isolation - Flag any query on customer data that is not filtered by the caller's org_id. Safe path: pass org_id into the WHERE clause. ``` Copilot's head-branch behaviour deserves a security reviewer's attention. GitHub's documentation says Copilot reads custom instructions from the branch with your changes, so you can test rule edits in the same pull request. It also means a pull request that edits `.github/copilot-instructions.md` gets reviewed under its own edited rules. ### Static Analysis Alongside the Model Several of these reviewers are hybrids of rules and models. CodeRabbit documents more than 50 third-party linters and security analysers, such as ESLint and Semgrep, which run in its sandbox and feed their results into the review. It picks tools by the files present, so PHP linters stay off a Java project. The scanner and model do different jobs. A linter can say with certainty that `org_id` is unused, and Ruff's ARG001 rule does exactly that when a project enables it, though it has no way to say why that matters. The model can connect the unused argument to the tenant filter in the next function, which is the finding a person needs to act on. GitHub keeps the two apart more visibly. Its documentation positions GitHub Code Quality, with rules-based CodeQL analysis and optional merge gating, as the complement to Copilot's model-based review. OpenAI's Codex guidance says the same thing from the other side: leave lint and other deterministic checks in CI and out of review rules. ### Model Passes and Verification The model stage is where products differ most, and where vendors say least about exact models. Copilot's documentation says code review uses a tuned mix of models and doesn't support model switching. Its Balanced effort level routes a pull request to a higher-reasoning model, and is the built-in default. Lite aims for faster feedback on common issues. Multi-pass designs are now common across the category. Anthropic describes Claude Code Review as several agents working in parallel, each hunting a different class of issue, followed by a verification step that checks candidates against actual code behaviour before anything posts. Qodo describes specialised review agents sharing one context layer, and CodeRabbit lists separate review and verification agents. Filtering is the other half of this stage. Codex posts only P0 and P1 issues on GitHub, so a quiet Codex review may simply mean nothing cleared that high bar. Greptile exposes a strictness setting from 1 to 3, where 1 comments on nearly everything and 3 keeps only critical issues. Effort settings let you trade money for review depth. On its usage-based plans, BugBot offers Low, Default, High, and a Smart mode that picks a level from your instructions, and Cursor's docs say higher effort can find more bugs at more cost. Greptile offers Base, Plus and Apex review modes with different credit costs. Compare the actual findings before paying for deeper analysis. ### Comments, Severity, and Check Runs Output formatting shapes how the team treats the findings. Claude Code Review tags each finding with a severity, and its Pre-existing tag marks a bug the pull request didn't introduce. That last category is useful, because it separates this author's regression from old debt the reviewer happened to spot. Check runs are where teams get surprised. Claude Code Review does not report a failure conclusion, so its findings do not block a merge by themselves. BugBot reports findings as `neutral` by default, and only reports `failure` when fail-on-unresolved behaviour is enabled. Even a finished check proves little on its own, because both vendors also report `neutral` when a run is cancelled or hits an error, and Anthropic's check does the same after a timeout with no findings posted. Read the check's title and summary before you treat it as a clean review. The approval side of review is shifting too. Copilot's reviews don't count toward required approvals by default, but GitHub now has a public preview in which Copilot can submit an approving review that satisfies the rule. It only applies once admins enable it at every settings level GitHub lists, and GitHub dismisses that approval when new commits arrive. ### Learning from Feedback This is the new hire learning which comments landed. Greptile learns from team comments, from replies to its own comments, from thumbs-up and thumbs-down reactions, and from whether a comment got addressed by the final commit. Its docs describe style comments fading for a team that never acts on them. BugBot generates learned rules from repository activity and accepts `@cursor remember` followed by a fact on any pull request. CodeRabbit keeps Learnings from your chat interactions with its bot. Anthropic collects thumbs reactions after merge to tune its reviewer, but its documentation says replies don't prompt Claude to respond or change the pull request. Learning from reactions cuts both ways for a team. A reviewer that suppresses a category because the team ignored it could suppress useful findings in that category. Review learned rules the way you review code, and delete the ones that encode a bad habit. ## What Data Does an AI Reviewer Access? An AI reviewer can access the code, related files and metadata permitted by its integration. Hosted reviewers typically need repository read access; posting comments or fixes requires additional permissions. Before you install one, trace where the code goes and what the reviewer executes on its servers. The answers differ by vendor and by plan. CodeRabbit's FAQ says code is shared with OpenAI or Anthropic for review purposes only and never used for training. It also says some data persists, including encrypted code caches and the vector index, along with Learnings. Setting `knowledge_base.opt_out` to true removes stored knowledge-base data, and self-hosted Enterprise customers can opt out of all retention. Anthropic's managed Code Review isn't available to organisations with Zero Data Retention enabled. Copilot's agentic context runs on GitHub-hosted runners by default, and teams can switch to self-hosted runners. Greptile documents self-hosted deployment on Docker Compose or Kubernetes for teams that need the index on their own servers. Execution is the risk that teams most often forget. In January 2025, Kudelski Security reported that CodeRabbit ran RuboCop on a pull request's own `.rubocop.yml`, which could load an attacker's Ruby file outside the sandbox. The researchers pulled environment variables, including API keys and the CodeRabbit GitHub App private key, which they said gave read and write access to about a million repositories. CodeRabbit confirmed a fix on January 30, 2025, moved RuboCop into its sandbox, and rotated credentials; Kudelski published the details that August. The lesson applies to every hybrid reviewer. Any tool that runs linters or tests on a stranger's pull request is running that stranger's configuration, so treat pull request content as hostile input and check how the vendor isolates it. Prompt injection is the model-level version of the same problem. A comment or file in the pull request can contain text aimed at the reviewer instead of the human reader. Keep AI review advisory on public repositories and grant the app the narrowest permissions it offers. On sensitive paths, a bot's approval should never stand in for an accountable human. ## What Determines AI Code Review Costs? AI code review costs depend on the billing unit, review depth and number of runs. Seat plans hide per-review cost behind a monthly fee and a rate limit, while usage plans charge per run or per credit, so every push and every effort upgrade shows up on the bill. Prices can change, so check the vendor's current pricing before you budget. Billing units and figures as each vendor publishes them for its own tool. Confirm current prices on each vendor's pricing page. | Reviewer | How It Bills | Vendor's Published Figure | | CodeRabbit | Per developer, with hourly review limits | Essentials $24/dev/mo, billed annually | | Greptile | Per active developer, plus credits | $30/seat/mo with 50 review credits | | BugBot | Usage per run on current plans | Credits consumed by each run | | GitHub Copilot | AI credits plus Actions minutes | $0.05 to $5 credits per review | | Claude Code Review | Usage credits outside plan limits | Token-based usage; cost varies by review level | | Codex | ChatGPT plan usage for GitHub reviews | Plus from $20/mo; API keys excluded | | Qodo | Shared workspace credit pool | Credit prices on Qodo's pricing page | Copilot's range in that table spans two effort levels. GitHub estimates $0.05 to $1 of AI credits for a Lite review and $0.25 to $5 for Balanced, before Actions minutes, and charges an automatic review to the pull request author. Greptile also bills the author, at $1 per credit once they pass their 50 included credits. For Codex, OpenAI's pricing page says GitHub code review needs a ChatGPT Plus, Pro, Business or Enterprise plan, because API-key access includes no cloud features. Reviews you run locally with Codex count toward general usage limits instead. Run the math against your own push habits. A team that pushes eight small commits to each pull request can pay for eight reviews on any per-push, usage-billed setting, since each push that triggers a completed review is billed. The head-to-heads listed in the [AI code review tools compared](/ai-code-review/comparisons/) hub put these figures side by side. ## A Worked Example Take the invoice function from the diff earlier and walk it through each stage. The code below reproduces the bug in a runnable form, using an in-memory SQLite database with two organisations. ```python import sqlite3 db = sqlite3.connect(":memory:") db.execute("CREATE TABLE invoices (id, org_id, total)") db.executemany( "INSERT INTO invoices VALUES (?, ?, ?)", [(1, 10, 120.0), (2, 20, 980.0)], ) def get_invoice(invoice_id, org_id): # New in this PR: org_id is passed in but never used return db.execute( "SELECT id, org_id, total FROM invoices WHERE id = ?", (invoice_id,), ).fetchone() # A user in org 10 asks for invoice 2 print(get_invoice(2, org_id=10)) ``` Running it prints `(2, 20, 980.0)`, which is invoice 2 from organisation 20, handed to a user in organisation 10. Each stage contributes something different when a pull request containing that function arrives. 1. Diff only: the added lines look tidy and parameterised, so a diff-only pass may say nothing at all. 2. Context: retrieval finds `list_invoices` and other queries that always filter by `org_id`, which marks the new function as the odd one out. 3. Rules: the tenant isolation rule above tells the model this pattern is a blocking issue in this repository. 4. Static analysis: an unused-argument rule flags `org_id`, which points the model to the unused argument. 5. Verification: a second pass confirms the query path is reachable and the argument never reaches SQL. 6. Output: a severe finding lands on the query line, ideally with the fix below as a suggestion. ```python def get_invoice(invoice_id, org_id): # Fixed: the query is scoped to the caller's org return db.execute( "SELECT id, org_id, total FROM invoices" " WHERE id = ? AND org_id = ?", (invoice_id, org_id), ).fetchone() print(get_invoice(2, org_id=10)) # another org's invoice print(get_invoice(1, org_id=10)) # this org's invoice ``` With the same table setup as before, the fixed version returns nothing for the other organisation's invoice and still finds its own: ```text None (1, 10, 120.0) ``` None of the stages alone makes the finding certain, and several of them lift the odds. That's why two reviewers with the same underlying model can perform very differently on your repository. ## What Can AI Code Review Catch and Miss? AI code review can catch logic errors, missing checks and cross-file inconsistencies when the relevant evidence reaches the model. It can miss defects that depend on unavailable context, and it can still misread code it has seen. Reviewers do well where the evidence is in text they can read: - Logic errors inside the changed lines, such as a wrong comparison or a missing null check. - Changes that break a pattern the index can see, like the missing tenant filter above. - Security smells with a visible source and sink, such as string-built SQL or a hard-coded secret. - Violations of rules you wrote down, especially short rules scoped to specific paths. They miss what never reaches the model. Production configuration, feature flags, live data, and deployment order often live outside the repository, or in files the reviewer never reads. Excluded files are invisible by design, so a Copilot review skips your lockfile entirely. Other repositories only count when the product supports linking them, as CodeRabbit's multi-repo analysis and Greptile's repo clusters do. Intent is the hardest blind spot to close. A reviewer can't know that a refund limit is a legal requirement unless a ticket or rule says so, which is why Qodo and Greptile pull linked tickets into context. Performance under real load is similar, since reading code rarely reveals how it behaves at ten thousand requests a second. Finally, a clean review is one sample. GitHub's own documentation says Copilot isn't guaranteed to spot every problem and tells users to validate its feedback. Every vendor in this cluster would say the same about its own tool. ## Common Pitfalls and Debugging Most complaints about AI review trace back to a stage that was left on its defaults. These are the ones that come up most in vendor troubleshooting pages, with the stage that causes each. ### The Bot Posts Too Many Comments Noise usually comes from the filter stage. Raise Greptile's strictness, or cap nits in Claude's `REVIEW.md`, which Anthropic's example limits to five per review. Then remove rules that duplicate your linter, since a model restating a lint error costs money and adds nothing. ### Your Rules Seem to Be Ignored Check the filename and its location before anything else. CodeRabbit matches guideline filenames case-sensitively, so `claude.md` doesn't match the `**/CLAUDE.md` pattern. Listing a guideline file under `path_instructions` does not load it as review guidance or add it to the review scope. Use the supported guideline configuration to supply its instructions. BugBot ignores Cursor's `.mdc` project rules, so put repository rules in `.cursor/BUGBOT.md`, team rules, or its learned and manual repository rules. It also truncates each rule at 30,000 characters and caps the combined rules at 100,000. Comment `bugbot run verbose=true` to see exactly which rules a run loaded and which were cut. ### The Review Never Ran Start with the trigger, since most silent reviewers never fired. A manual Codex review needs the exact `@codex review` text, and automatic Codex reviews only run once Automatic reviews is switched on in Codex settings, and CodeRabbit's plans page says public repositories with fewer than ten stars need a manual trigger. For Copilot on an organisation licence, the organisation must enable code review in its Copilot policies, and a reached budget blocks reviews along with other credit-consuming features. ### The Check Passed with Open Findings This is the neutral-conclusion behaviour from earlier. If you want merges gated, enable BugBot's fail-on-unresolved option where it's available, or parse the severity counts Anthropic writes into the Claude Code Review check run from your own CI job. Keep a human approval on the branch rule either way. ## How Should You Evaluate an AI Reviewer? Evaluate an AI reviewer by checking whether it finds known defects in your code, how much time its incorrect comments waste, and what each useful review costs. Its access to context matters more than a long feature list. Architecture trade-offs across the reviewers in this cluster. | Review Approach | What It Does Well | Main Cost | | Diff-focused, strict filter | Few, high-priority comments | Misses cross-file breaks | | Indexed repository | Catches pattern and caller breaks | Stored index of your code | | Linter and model hybrid | Deterministic anchors for findings | Runs untrusted configuration | | Multi-agent with verification | Fewer wrong findings posted | Slower and pricier per review | To evaluate a product, test the stages instead of the marketing. The hub's [four-week rollout method](/ai-code-review/#roll-out-one-reviewer) covers sampling and measurement; add these mechanism checks to it: 1. Replay a past pull request whose bug sat in a file outside the diff, which tests context gathering. 2. Add one rule, then open a pull request that breaks it, which tests rules loading. 3. Reject the same style comment three times and watch whether it fades, which tests learning. 4. Push several small commits and read the bill afterwards, which tests the trigger and cost model. 5. Ask the vendor where the index and learned rules are stored, and how you delete them. Score only findings that a person has verified. A reviewer that posts two verified bugs a week beats one that posts forty comments you've learned to scroll past. ## My Verdict AI code review is a pipeline wrapped around a model. The trigger sets how often it runs and what you pay, context sets what it can see, rules tell it what matters here, static analysis supplies results from repeatable checks, verification cuts the wrong answers, and feedback shapes the next review. Those stages give you a useful place to investigate a miss, although the model can still reason incorrectly from complete context. My verdict, as a security reviewer, is that context and rules matter more than the model name on the box. Pick the reviewer whose context reach matches your codebase, write three sharp rules on day one, and keep its check advisory until your own tests justify letting it block merges. Then build trust gradually, as you would with a new senior hire, by checking its work on your code. ## Frequently Asked Questions ### Does an AI code reviewer read my whole repository? An AI code reviewer may retrieve files from across your repository, but that does not mean every file reaches the model in every review. Repository indexing, file exclusions and the review mode determine what the reviewer can inspect. ### Is my code used to train an AI review model? Whether source code is used to train an AI review model depends on the provider and your agreement. Check the applicable data policy before connecting a repository. Training, temporary processing and retained indexes are separate questions; a no-training promise does not mean no storage. ### Why can AI reviews of the same pull request produce different comments? AI reviews of the same pull request can produce different comments because model outputs vary and each run may retrieve different context. Incremental reviews may also examine only newer commits. Compare verified findings rather than treating a single quiet review as proof that the code is correct. --- ## Best AI Code Review Tools (October 2026) URL: /ai-code-review/ Cluster: AI Code Review > Overview & AI Code Review AI code review tools inspect proposed code changes and flag bugs, security risks and missing tests before you merge them. Bugbot is our current pick, with Codex close behind. On our pull requests, Bugbot catches more high-severity issues and edge cases; Codex earns its place by finding different mistakes, including inaccurate content. The useful reviewer is the one whose comments your team acts on. A subtle defect can justify months of fees. Pages of unnecessary suggestions can teach developers to ignore the bot entirely. Our team uses Bugbot, Codex and Claude Code to review pull requests across our projects. We used CodeRabbit earlier, and it still holds up well. The Greptile, Qodo, Graphite, and Copilot sections draw on each vendor's documentation and pricing. ## Our Picks for October 2026 These picks reflect what our team saw on its own pull requests through September 2026. We have used the four tools ranked below. Our assessment of the other tools relies on vendor documentation. 1. Bugbot, slightly ahead. It is the stronger of our two main reviewers on high-severity and security issues, and it flags more edge cases. 2. Codex, a close peer. It catches different things, including content inaccuracies before merge. It also spends tokens on less critical points, and our usage has felt tighter since OpenAI's plan changes. 3. Claude Code, also in our review process. We run its review beside the other two, and its local command is part of our routine before a pull request opens. 4. CodeRabbit, still good. It remains a strong broad reviewer for a team that wants deep configuration. The gap between Bugbot and Codex is small. Because they catch different things, we keep both in our review process. ## Which AI Reviewer Fits Your Team? Bugbot is my starting point for teams focused on serious bugs and security issues. CodeRabbit offers broader feedback that you can tune to your team’s rules. Start with Codex or Copilot if your existing subscription already covers the review workflow you need, then compare the additional usage cost. Starting prices; usage charges and plan limits vary. | Tool | Review | Entry Price | Best For | | [Bugbot](/ai-code-review/bugbot/) | Cursor service | Usage credits; account terms vary | Finding serious bugs and security issues | | [Codex for code review](/ai-code-review/codex/) | Coding agent | ChatGPT plan usage | Teams already using Codex | | [Claude Code for code review](/ai-code-review/claude-code/) | Coding agent | Plan or usage credits | Custom local and managed reviews | | [CodeRabbit](/ai-code-review/coderabbit/) | Dedicated bot | Essentials $24/dev/mo (annual) | Broad, configurable pull-request feedback | | [GitHub Copilot](/ai-code-review/github-copilot/) | GitHub service | Paid plans plus AI Credits | Teams already using GitHub Copilot | | [Greptile](/ai-code-review/greptile/) | Context bot | Free Starter | Cross-file context and custom rules | | [Qodo](/ai-code-review/qodo/) | Dedicated bot | Pro Team from $30/mo | Shared team rules and usage | | [Graphite Agent](/ai-code-review/graphite/) | Review platform | Free Hobby | Teams using stacked pull requests | These are starting prices. Your actual bill depends on team size, review volume and what your existing plan includes. Some tools charge per developer; others charge for usage. Larger changes and repeated reviews can push the cost up. ## What Does AI Code Review Do? AI code review reads changed code and related files to flag possible bugs. Traditional checks follow fixed rules and give the same result when their inputs and setup stay the same. AI reviewers also interpret code and written instructions, so they may spot problems those rules miss. Their findings can vary between runs and still need a developer to check them. Most products operate in two main forms. Dedicated reviewers install as Git-host applications and comment automatically on pull requests. Coding agents run locally, through a managed review service, or inside a CI workflow with custom instructions. A dedicated reviewer handles when reviews run, where comments appear and how feedback is stored. With a coding agent, your team may need to set up the triggers, instructions, permissions and repeat reviews itself. For what happens inside a single review, from the diff the tool reads to how it decides which findings to post, see [how AI code review works](/ai-code-review/how-ai-code-review-works/). Neither replaces tests or a person deciding whether to merge. The reviewer should explain problems such as a missing permission check, a broken function call, a risky migration or a test that no longer checks the right behaviour. A developer must confirm the problem and decide whether the change can ship. ## How Did We Evaluate These Reviewers? We evaluated AI reviewers by the usefulness of their findings, the code they can inspect, their fit with our review process and the controls they give a team. A cheap subscription offers little value if nobody reads its comments. Bugbot, Codex, Claude Code and CodeRabbit reflect our experience on our own pull requests. Greptile, Qodo, Graphite and Copilot are assessed from official documentation and pricing. We have not tested all eight tools side by side. | Evaluation Criterion | Practical Evidence of Value | Warning Sign During Review | | Finding quality | A bug you can reproduce and confirm | A confident claim you cannot reproduce | | Repository context | Checks how the change affects related code | Claims to read the whole project but uses outdated code | | Workflow fit | Reviews run when needed and comments are easy to act on | Developers repeatedly copy findings between tools | | Team control | Control over rules, excluded files, data and spending | Unwanted comments or permissions nobody manages | ## AI Code Review Tools Compared ### 1. Bugbot Best for: Finding serious bugs and security issues. [Bugbot](/ai-code-review/bugbot/) is Cursor's pull-request reviewer, and it tops our list this month. On our pull requests it is slightly ahead on high-severity and security issues, and it flags more edge cases than the other reviewers we run. I like that it puts the most damaging mistakes first. Cursor's documentation now lists GitHub, including Enterprise Server, plus GitLab, Bitbucket, and Azure DevOps as supported hosts. Effort runs Low, Default, High, or Smart, and Autofix can start a Cloud Agent to push a fix on supported hosts. Developers can also run Bugbot locally with the `/review-bugbot` skill in Cursor 3.7 or later and in the Cursor CLI. Cursor moved Bugbot to usage billing in May 2026, and its announcement estimated an average run at $1.00 to $1.50. That is a historical estimate, not a current fixed tariff. Cost varies with pull-request size and complexity, so how often you run it matters to the bill. ### 2. Codex Best for: Teams already using Codex. Codex cloud connects to GitHub and accepts the literal `@codex review` trigger. OpenAI's documentation says it flags P0 and P1 issues by default on GitHub and reads review rules from a `## Code Review Rules` heading in `AGENTS.md`. It can also run automatically, and `@codex security review` requests a security-focused pass. OpenAI added a Code Review view to the ChatGPT desktop app at DevDay on September 29, 2026. It shows summaries and diffs, lets you ask Codex about a change, and submits your review as Comment, Approve, or Request changes. GitLab merge request support is in preview, and automatic cloud reviews no longer need a cloud environment. Codex is a close second for us. It catches content inaccuracies before merge, which matters on a site where a wrong price is a defect. The trade-off is that it sometimes spends tokens on points below critical, and our review usage has felt tighter since OpenAI's plan changes. The [Codex review guide](/ai-code-review/codex/) covers rerunning a review after new commits, instruction scope, and the separate `codex-action` route. ### 3. Claude Code Best for: Custom local and managed reviews. Claude Code can review locally, run through the official GitHub Action, or use Anthropic's managed Code Review service. The managed service runs several agents on a pull request and a verification step that filters false positives. Teams tune it with `REVIEW.md`, and it never approves or blocks the pull request. Anthropic limits managed review to Team and Enterprise plans and excludes Zero Data Retention organisations. It bills token-based usage credits outside plan limits. Quick, Standard and Deep review levels change the depth, time and cost; no single per-review price applies. Locally, `/review` is now an alias of `/code-review`, and `/code-review ultra` starts a deeper cloud review. We run Claude Code review in the same review process as Bugbot and Codex. What I like about its local command is that it runs as a background subagent, so the review stays out of the session doing the work. The [Claude Code review guide](/ai-code-review/claude-code/) separates the managed, Action, and local paths. ### 4. CodeRabbit Best for: Broad, configurable pull-request feedback. [CodeRabbit](/ai-code-review/coderabbit/) is a broad dedicated reviewer with configurable review profiles, path instructions, and organisation controls. It ran in our review process before the current setup, and it is still a good choice. It checks bugs, tests, documentation and how easy the code is to maintain. You will need to adjust its settings so useful warnings do not get buried in minor suggestions. CodeRabbit's plans page lists Essentials at $24 per developer per month billed annually, or $30 monthly. Team costs $48 per developer per month billed annually, or $60 billed monthly. Advanced costs $72 or $90 per developer per month on the same billing terms and adds continuous pull-request security review. Included review allowances vary by tier, with rolling limits and optional paid overages. ### 5. GitHub Copilot Code Review Best for: Teams already using GitHub Copilot. [Copilot code review](/ai-code-review/github-copilot/) stays inside GitHub and the major IDEs, so teams already paying for Copilot have less to set up. GitHub's documentation now describes two effort levels. Balanced is the built-in default. Lite costs an estimated $0.05 to $1 in AI Credits per review, while Balanced routes to a higher-reasoning model at $0.25 to $5. Reviews also consume GitHub Actions minutes when the service gathers project context. The bigger change in September was approvals. Since September 1, 2026, a public preview lets admins allow Copilot to submit an approving review that satisfies a required-approval rule. It is off by default, can be limited to file paths, and is dismissed when new commits arrive. I would leave that setting off until someone on the team takes responsibility for checking bot-approved merges. ### 6. Greptile Best for: Cross-file context and custom rules. [Greptile](/ai-code-review/greptile/) emphasises a repository-wide code graph and custom rules. It maps how code connects across the project, which may help it spot bugs that a review of changed lines alone would miss. Try it on known bugs involving several files to check whether that map is up to date and your rules work. Greptile's pricing lists a free Starter plan for one active developer with 50 credits a month. Pro costs $30 per seat per month with 50 included credits and $1 per extra credit. A Base review uses one credit, Plus uses three, and Apex uses ten, so the chosen level sets the bill. ### 7. Qodo Best for: Shared team rules and usage. [Qodo](/ai-code-review/qodo/) offers pull-request review across several Git providers, shared team rules, and controlled enterprise deployment. The team shares a pool of credits instead of buying a seat for every developer. That can make a small trial easier to budget. Qodo's Pro Team starts at $30 per month with a shared credit pool, a 14-day trial and usage priced at $0.012 per credit. Its recommendation for teams of up to 30 users describes the intended fit, not a hard user cap. Review volume and enterprise requirements determine the appropriate plan. ### 8. Graphite Agent Best for: Teams using stacked pull requests. [Graphite Agent](/ai-code-review/graphite/) belongs to a wider pull-request platform built around stacked changes, review, and merge coordination. Its AI reviewer can use related pull requests in the stack and your organisation’s instructions. That makes it worth considering if you already want Graphite for managing pull requests. If you only need AI comments, compare the whole platform’s price with a separate reviewer. Graphite's pricing page lists a free Hobby plan with limited AI reviews and Starter at $20 per user per month billed annually. Team costs $40 per user per month billed annually and includes unlimited AI reviews. ## Coding Agents as Reviewers Coding agents can review changes before you open a pull request. You can give them specific instructions and ask them to investigate further. Your team must configure that flexibility by choosing the model, which tools it can use, where it reports findings and whether it may change code. The separate [coding agents as reviewers guide](/ai-code-review/coding-agents-as-reviewers/) compares local, managed, and GitHub Action workflows. Codex and Claude Code also offer managed review services, with different features and costs. ## Can AI Review Code Before a Pull Request? Codex, Bugbot, CodeRabbit and Claude Code can review local changes before you open a pull request. Running a review while you still have the code in mind makes findings easier to investigate. A local review can still miss defects, so keep the shared review and tests before merging. Commands and billing from each vendor's official docs. | Tool | Local Command | What It Draws On | | Codex CLI | `/review` or `codex review` | General Codex usage limits | | Cursor Bugbot | `/review-bugbot` skill | Cursor usage, not itemised | | CodeRabbit CLI | `cr review` | Plan's hourly review limit | | Claude Code | `/code-review` | Counts toward normal plan usage | Codex's `/review` reads a diff against a base branch, uncommitted changes, or one commit, and it leaves the working tree alone. OpenAI's pricing page says Code Review usage applies only to reviews run through GitHub. Local reviews count toward general Codex limits instead. Bugbot's local skills run in Cursor 3.7 and later and in the Cursor CLI. Cursor's docs don't itemise what a local run costs, so watch the team's usage dashboard during a trial. CodeRabbit's Free plan allows three CLI reviews per hour, while its Free pull-request tier provides summaries rather than full reviews. Paid limits and fair-use terms vary by tier. The CLI supports `cr review --deep`, and an `--agent` output mode lets Claude Code or Codex read the findings. Claude Code's `/code-review` takes an effort level, applies findings with `--fix`, and posts them to a GitHub pull request or GitLab merge request with `--comment`. The cloud `ultra` mode is the paid exception, with three free runs on Pro and Max, then roughly $5 to $25 per review in usage credits. What I like about a local pass is that a mistake gets fixed before anyone else reads the diff. What I dislike is that the result stays private, so a confirmed finding needs a test or a pull-request note before a teammate can trust it. ## Other Review Options - Cursor Security Review: launched on September 23, 2026 for Teams and Enterprise plans. It reads each pull request in codebase context and posts one comment on exploitable bugs, while style and quality stay with Bugbot. - Devin Review: Cognition's reviewer is free on public pull requests; self-serve private reviews consume on-demand credits; Enterprise reviews draw from an ACU pool. Its docs list GitHub, GitLab, and Azure DevOps as supported hosts. - Gemini Code Assist: Google shut down the free consumer GitHub app on July 17, 2026. The separate GitHub enterprise Preview remains documented by Google; the IDE subscription changes should not be confused with that service. - Gitar: Sonar acquired this AI reviewer on May 21, 2026. Gitar commits fixes and iterates until CI passes, which puts it in a different risk class from comment-only bots. - Ellipsis: it now presents itself as a managed coding-agent platform, with code review as one use among several. ## What Bugs Can AI Reviewers Catch? AI reviewers can catch bugs such as a missing null check, an unhandled error or a permission check that runs after private data has already loaded. They may also spot calls to a function that has changed, or a database migration that deletes data with no way to recover it. Reading related files can help an AI reviewer catch more bugs. It might spot that a database change breaks the code that formats its output, or that retrying a webhook could process the same payment twice. You can also ask it to check specific risks, such as one customer accessing another customer’s data or a change breaking older clients. AI reviewers can spot missing tests when code changes. A useful comment explains what needs testing and which bug the test could catch. “Add more tests” leaves you guessing. Check security warnings as carefully as any other bug report. What can an attacker control? Can that input reach the vulnerable code, and do existing checks stop it? A warning should explain how the attack works and what damage it could cause. Otherwise, you may spend hours investigating a problem that does not exist. ## Where Does AI Code Review Fall Short? AI code review can miss defects when requirements, related code or production behaviour are absent from its context. It can also report a problem that does not exist. Common gaps include: - The changed lines may not explain what a customer was promised, which migration runs first or why the team chose a particular approach. - Feature flags, production data, external services, permissions, and deployment order may sit outside the repository context. - A tidy-looking refactor can remove a deliberate separation between components or introduce a risky dependency. - A completed review with no comments means the tool found nothing to report. It does not prove the code is safe. Give the reviewer access to the requirements and write down rules the code must always follow. Limit its permissions to what the review needs. Before accepting a refactor, ask which problem it fixes. ## How Does AI Review Complement Static Analysis? AI review looks for context-dependent mistakes, while static analysis applies configured rules to source code. Keep type checks, tests and security scanners alongside the AI reviewer; their results are reproducible when their inputs, versions and configuration stay fixed. Paying an AI reviewer to repeat formatting or type errors wastes usage and adds comments your existing checks already cover. Run AI review after the basic checks pass, or keep its comments separate from their results. That leaves the bot to look for bugs those checks miss. When it finds a real bug, add a test so future changes are checked for the same mistake. | Review Layer | Primary Review Job | Recorded Result | | CI | Fixed checks and tests | Pass or fail against those checks | | AI reviewer | Bugs the existing checks may miss | Possible bugs to investigate | | Developer | Requirements and judgement | A decision on whether to merge | ## Roll Out One Reviewer Choose one repository with active pull requests and known failure patterns. Start in manual mode if the product allows it. Select a mix of authentication, data, API, user-interface, and maintenance changes so the trial does not reward one narrow specialty. 1. Replay several historical pull requests with known defects and known false alarms. 2. Enable the reviewer on current work without changing human approval rules. 3. Disable generated files, summaries, and categories the team consistently skips. 4. Add three to five repository rules tied to costly, concrete failures. 5. Review the evidence after four weeks before expanding repositories or triggers. Keep automatic reruns off until the billing and comment behaviour are understood. Some tools review every push, some remain subscribed to later pull-request updates, and some charge for each run. Even a tiny fix can trigger several nearly identical reviews. Assign one named owner for reviewer tuning and configuration changes. That person should collect recurring false positives, remove stale rules, inspect missed defects, and publish changes to the configuration like ordinary code. If nobody owns this work, the same unwanted comments keep coming back. ## How Should You Measure AI Review Value? Measure AI code review by confirmed defects found, important defects missed, time spent checking comments and total cost. Classify each finding as independently useful, already caught by CI, already caught by a person, incorrect, or too vague to act on. Record severity only after verification so a bot's label does not inflate the result. Measure the cost imposed on reviewers as well as subscription spend. Time spent reading summaries, disproving warnings, waiting for slow runs, and rerunning after pushes belongs in the calculation. Time saved finding and explaining a real defect belongs on the other side. Count the bugs the AI missed, too. Check what people found later, which tests failed and what broke in staging or production. A quiet reviewer that catches a serious bug can be more useful than a busy one that repeats what you already know. At the end of the trial, keep the results and decide whether to keep the reviewer, adjust its settings, replace it or stop using it. Treat vendor benchmarks as a reason to investigate a tool. Compare the dataset, tool versions and settings before using a percentage to choose a reviewer. A high catch rate without a false-positive count tells only part of the story. ## The AI Code Review Verdict Bugbot is our pick for October 2026, by a small margin. It is slightly ahead on high-severity and security issues and flags more edge cases on our pull requests. Codex is the close second, and it earns its place by catching different things, including content inaccuracies before merge. Claude Code rounds out our review process, and its local review is part of how we work before a pull request opens. CodeRabbit is still the dedicated reviewer I would suggest for a team that wants broad coverage and will own the configuration. GitHub Copilot is a sensible first test for a team that already pays for Copilot. For any other team, the best reviewer is the one that finds enough useful bugs to justify its bill and the time you spend checking its comments. Remove a tool that merely repeats CI or human comments. When the shortlist is down to two, use our [AI code review comparisons](/ai-code-review/comparisons/) to look at the differences. ## Frequently Asked Questions ### Should I run more than one AI code review tool? Run more than one AI code review tool when a second reviewer consistently catches important defects the first misses. We keep Bugbot and Codex together because their findings differ on our pull requests. Start with one reviewer and measure that gap before paying for another. ### Does a local AI code review stay on my computer? Running an AI review command locally does not necessarily keep your code on your computer. A command-line tool can send code to a hosted model or review service. Check the selected provider, data-retention terms and network access before reviewing private code. ### Can an AI code reviewer approve a pull request? GitHub Copilot can submit an approving pull request review when an administrator enables its approval preview. That approval can satisfy a required-review rule, subject to the configured limits, and is dismissed when new commits arrive. Claude's managed review does not approve pull requests; the Codex review interface lets a person submit an approval. Your team should still name who is responsible for the merge. --- ## Qodo Code Review Guide (2026) URL: /ai-code-review/qodo/ Cluster: AI Code Review > Overview & AI Code Review Qodo is an AI code review platform for teams that want pull-request findings tied to repository context and written engineering rules. Its current platform runs several review agents, ranks findings, and reports them inside the Git workflow. Older pages call the product Qodo Merge, and many developers still connect it with PR-Agent. Qodo now treats the hosted platform and the open-source project as separate paths, which affects features, ownership, pricing, and deployment. I have not run a new hands-on Qodo test; these product details come from vendor documentation and the public PR-Agent project. Compare Qodo with the other [AI code review options](/ai-code-review/) if you mainly need a simpler pull-request bot. ## Qodo at a Glance Before subscribing to Qodo, check the current plan limits and whether it supports your Git provider. | Area | Current Position | Buyer Check | | Main product | Qodo hosted platform | Trial on live PRs | | Review method | Multiple review agents | Useful findings | | Context | Repo, history, rules | Cross-repo contracts | | Open source | Separate PR-Agent project | Operator ownership | | Hosted price | Pooled monthly credits | Actual PR volume | | Enterprise | Single tenant or on-prem | Contract details | ## What Is Qodo? Qodo is an AI code review platform that combines pull-request analysis, repository context and shared engineering rules. It is most relevant when a team needs consistent review across several repositories, rather than another general coding assistant. Qodo’s Software Map helps trace relationships between services and repositories, while its Agentic Toolbox brings review into coding-agent workflows. PR Triage and PR Insights launched in research preview with Qodo 3.0. Confirm availability for your plan and deployment. Qodo’s official IDE extension listing says the plugin will retire on October 30, 2026, with local review moving to the Agentic Toolbox. New adopters should follow the current Toolbox setup rather than build a workflow around the retiring plugin. ## How Does Qodo Differ from PR-Agent? Qodo is a commercial platform, while PR-Agent is a separate MIT-licensed project you can run and modify yourself. They share a history, but features and deployment responsibilities are no longer interchangeable. Hosted Qodo supplies the managed service, context layer, rule controls, reporting, and paid deployment choices. The open-source PR-Agent project supplies code a team can inspect, run, alter, and connect to its own model provider. The two options require different work from your team. A hosted trial asks whether Qodo’s findings improve a team’s pull requests with tolerable noise and credit use. A PR-Agent trial also asks who owns the runtime, provider keys, webhook security, logs, updates, and failed jobs. | Question | Hosted Qodo | PR-Agent | | Who runs it? | Qodo | Your team | | How is it paid? | Credits or contract | Infra and model use | | Who updates it? | Qodo | Your operator | | Where are rules kept? | Qodo Rule System | Project configuration | | Best first test? | 14-day trial | Sandbox repository | Open source removes a software licence fee from one line of the budget. It leaves the rest of the operating bill in place. A team without a named owner for the self-hosted service should test the managed product first. ## How Does Qodo Review a Pull Request? Qodo reviews a pull request with specialised agents that inspect the change using repository context, history and engineering rules. They share repository context, PR history, and organisation rules, then produce ranked findings that explain the issue, its effect, and a path to a fix. The design matters when a diff looks harmless by itself. A renamed function may break callers elsewhere, a schema edit may violate another service’s contract, or a familiar pattern may have failed in a past PR. Repository and history context give the reviewer evidence beyond the touched lines. Qodo’s claims about recall, precision, and low noise come from Qodo. A buyer should verify them against known bugs from its own repositories. Test a bug involving several files, valid code that might attract a false warning, a missing test and a rule with a clear pass condition. A person still makes the final review decision. A ranked comment can explain code behaviour while missing product intent, release timing, or an external dependency. The human reviewer owns the merge and checks whether a suggested fix preserves the contract around the code. ## How Much Does Qodo Cost? The Qodo pricing page lists a 14-day no-card trial, pooled credits at $0.012 each, and no permanent general-purpose hosted free tier. Pro Team starts at $30, while Enterprise uses negotiated pricing. | Plan | Price | Allowance | Best Fit | | Trial | $0 for 14 days | Unlimited trial credits | Measured pilot | | Pro Team | From $30/mo | 2,500 pooled credits | Small teams | | Larger packs | $0.012 per credit | 5,000 or 20,000 | Higher PR volume | | Enterprise | Custom | Contract terms | Governance or private deployment | Qodo estimates 2,500 credits at about 18 reviews, 5,000 at about 36, and 20,000 at about 144. PR size and difficulty alter credit use. Unused credits expire each month, and reviews can enter paid overage until a team-set spending cap stops them. Qodo markets Pro Team towards teams of up to 30 users, but its pricing FAQ says there is no hard user limit. Treat that figure as guidance on fit rather than a seat cap. Estimate Qodo’s cost from your pull requests. Count monthly PRs, split them by size, and include repeat reviews after new pushes. The free trial should reveal the average and worst credit use before you choose a pack. ## How Does Qodo Use Your Team’s Review Rules? Qodo uses written requirements, repository patterns and previous pull-request discussions to build and apply review rules. The system can find duplicate or conflicting rules, apply them to selected repositories, and report when they are used or broken. Write a rule that names the unsafe operation and the check that must prevent it. A payment-webhook rule should name signature verification, its position before business-field parsing, and the test or code path that proves compliance. This structure gives Qodo a stable condition to evaluate on every run. Start with rules human reviewers already enforce. Good first candidates include authentication boundaries, generated paths that should be skipped, migration ordering, and API compatibility. Give each rule an owner who can resolve conflicts and remove it when the code or policy changes. Rule reporting can expose a process problem. If developers bypass the same valid finding every week, either the rule lacks authority or the delivery process makes compliance too costly. If the rule draws many rejected comments, narrow its scope and add contrasting examples. ## Which Git Providers and Hosting Options Does Qodo Support? Qodo's provider setup index lists GitHub, GitLab, Bitbucket, and self-hosted editions. Its 2026 changelog adds guided Bitbucket Cloud and Azure DevOps setup, while enterprise material also names Gerrit. Check that your chosen plan supports your Git provider. Azure DevOps arrived as an enterprise feature in Qodo 2.1, and some single-tenant or on-prem paths need vendor help. A logo on a provider list does not prove every Qodo feature works in every cloud and self-hosted edition. Ask the vendor to show the exact combination you will buy: provider, cloud edition, hosting model, authentication method, comment workflow, rule availability, and ticket connection. Run the same known-bug PR through that setup before contract approval. Enterprise deployment adds choices such as single-tenant software, on-prem operation, customer-held model keys, SSO or SAML, and audit logs. These are pricing-page claims. Review the contract, trust material, data flow, retention terms, and failure behaviour with your security team. ## How Does Qodo Handle Your Code? Qodo says customer code is processed for reviews and is not used to train models. Retention and deployment terms still need checking for the service you buy. Its public site also names strict retention, customer-held model keys, private deployments, and air-gapped enterprise use. Turn each claim into a contract question. Ask which pull-request data is stored, how long findings and embeddings remain, where logs live, which model providers receive code, and whether customer-held keys alter retention. Include ticket text and repository history because context can reach beyond the diff. Installation permissions deserve the same level of scrutiny. The app needs enough access to read code and post findings. Confirm who can add repositories, alter rules, raise an overage cap, view findings across teams, and change the provider connection. ## Test Qodo's Rules and Credit Pool Use the [hub's four-week method](/ai-code-review/#roll-out-one-reviewer) for the shared sample and outcome labels. Add two Qodo-specific questions: does the Rule System change a known result, and how many pooled credits do different types of pull request use? 1. Rule check: replay one known issue before and after adding a precise engineering rule. 2. Conflict test: add two intentionally overlapping rules and inspect Qodo's conflict handling. 3. Credit range: compare a small fix, a database edit, and a cross-repository contract change. 4. Overage stop: set a spending cap and confirm how a blocked review appears to authors. ## What Does Qodo’s Findings Dashboard Show? Qodo’s Findings dashboard gathers review findings across repositories and shows trends, including whether findings were fixed or bypassed before merge. It helps team leads spot recurring problems; a high count alone does not show whether code quality improved. A high finding count can mean risky code, an over-broad rule, or a new reviewer learning where to look. Read it beside pull-request volume, accepted comments, bypass reasons, and production defects. One chart cannot separate those causes on its own. ## What Are Qodo’s Main Limitations? Qodo’s main limitations are variable credit use, the work of maintaining useful rules and differences in feature availability between plans and deployment types. Confirm availability in your chosen provider and hosting model. Rules can spread a bad standard as quickly as a good one. Give each rule a named owner, a reason, examples, and a review date. Track accepted findings and bypasses, then remove rules that produce debate without code fixes. Credit pricing also rewards smaller pull requests because size affects use. That can align with good review habits, yet it makes forecasting harder for teams with large generated diffs or broad migrations. Trial those cases instead of using the vendor’s average as your budget. ## My Verdict Qodo suits teams that want review findings, centrally governed rules, and cross-repository reporting in one paid system. A small team can start with a shared credit pool. Keep paying only if the rules and review reports help the team find and fix problems. Choose PR-Agent only if someone on your team will run it and maintain its security. ## Frequently Asked Questions ### Can a team self-host PR-Agent? A team can self-host PR-Agent and choose its model provider and infrastructure. PR-Agent is separate from the commercial Qodo platform, and the team remains responsible for credentials, updates, webhooks and operating costs. ### Does Qodo have a free plan? Qodo offers a 14-day hosted trial rather than a permanent general-purpose free tier. Qualified open-source projects can apply for free access. Self-hosted PR-Agent has no software licence fee, but model use, hosting and maintenance can still cost money. --- ## Cursor BugBot vs GitHub Copilot Code Review URL: /ai-code-review/comparisons/bugbot-vs-github-copilot/ Cluster: AI Code Review > Comparisons Try Copilot code review first if your team already uses paid Copilot on GitHub. Choose BugBot if you want Cursor's review rules and direct repair handoff, or need its wider Git-host support. Both can use repository context, suggest fixes and run automatically; neither removes the need to check its findings. When [comparing review workflows](/ai-code-review/comparisons/), consider where you manage the work. BugBot uses Cursor's settings and usage billing. Copilot shares GitHub policies, AI credits and Actions infrastructure. For other choices, see our [AI code review tools comparison](/ai-code-review/). The detailed setup, rule files, and spending controls already live in the [BugBot guide](/ai-code-review/bugbot/) and [GitHub Copilot review guide](/ai-code-review/github-copilot/). ## BugBot and Copilot at a Glance For current product details, see [Cursor's BugBot documentation](https://docs.cursor.com/bugbot), [Cursor's usage-billing update](https://cursor.com/blog/may-2026-bugbot-changes), and [GitHub's current review overview](https://docs.github.com/en/copilot/concepts/agents/code-review). Preview features and usage terms can change. | Dimension | Cursor BugBot | GitHub Copilot | | Product home | Cursor dashboard | GitHub and editors | | Repository context | Diff plus rules and tools | Agentic project gathering | | Manual trigger | bugbot run | Request Copilot reviewer | | Project guidance | BUGBOT.md | Instructions and skills | | Repair handoff | Cursor or web agent | Copilot cloud agent | | Review effort | Configurable effort | Lite or Balanced | | Review charge | Usage per run | AI Credits and Actions | | Merge authority | Finding, not approval | Comments by default; approval preview | ## Where Do BugBot and Copilot Run Code Reviews? BugBot runs through connected Git hosts, while Copilot reviews GitHub pull requests and supports Azure DevOps in public preview, alongside editor and CLI interfaces. Configure BugBot in Cursor's dashboard and Copilot in GitHub's review settings. On GitHub, the documented manual comments include `cursor review`, `bugbot run`. Automatic mode can review pull-request updates, while personal settings can restrict a run to mentions or one pass per pull request. BugBot posts its finding on the pull request, with links to fix it in Cursor. "Fix in Cursor" opens the issue in the editor. "Fix in Web" sends it to Cursor's web agent. That path suits a team whose review and repair work already passes through Cursor accounts and its dashboard. Copilot starts from GitHub's Reviewers menu or an automatic-review ruleset. Its comments behave like ordinary review comments, including replies, reactions, resolution, and suggested changes. GitHub says the review usually returns in under 30 seconds, though the actual time depends on the repository and selected effort. Copilot's agentic parts run through GitHub Actions. Project-context gathering and the handoff to Copilot cloud agent use a runner, which gives administrators control over hosted, larger, or self-hosted compute. A failed or unavailable runner still produces a reduced review. Test that fallback before relying on automatic reviews. ## Can BugBot and Copilot Review Beyond the Diff? Yes, BugBot and Copilot can both use context beyond the diff. GitHub's current documentation describes a reviewer that can gather full project context, explore repository files with agentic tools, use custom instructions, and consult relevant agent skills or connected MCP servers. Copilot offers Lite and Balanced review effort. Balanced is the current built-in default unless another setting overrides it. Review-specific setup steps and runner controls determine whether full-project context gathering succeeds. The products still gather context through different paths. BugBot always includes the root `.cursor/BUGBOT.md` and can add more-specific files while it walks upward from changed files. Cursor also documents learned rules from pull-request feedback and MCP access for Teams and Enterprise accounts. Copilot reads several repository instruction formats and now reads them from the pull request's head branch. That makes a guidance change testable before merge. Its project gathering depends on the agentic runner path, so an organisation with disabled hosted runners must configure a working self-hosted route or accept the limited fallback. Both tools use more than the diff. BugBot applies Cursor's repository rules and learned preferences. Copilot assembles context during the GitHub review run and can execute setup steps in that environment. Choose the setup your team can check, afford and maintain. ## Can You Choose the Model for BugBot or Copilot Review? Neither BugBot nor Copilot code review exposes the same model choice as a general coding chat. GitHub describes Copilot review as a purpose-built mix of models, prompts and system behaviour. Reviewers cannot switch its model, and an organisation's Copilot Chat model settings do not control the review product. A new model release elsewhere in the Copilot stack says nothing reliable about the review that commented on a pull request. BugBot exposes effort rather than a public model picker for pull-request review. Default effort preserves Cursor's normal pass, while higher effort spends more time and usage on the change. The setting a team can control is how much review work to buy for a given pull request. A model name alone will not tell you which reviewer to buy. Record the product, effort level, date, repository state, and confirmed finding instead. That record shows what the reviewer found, even if the vendor later changes its model. The same rule applies when an attractive benchmark arrives. Vendor tests can explain what a product team tuned for, though they rarely reproduce your languages, rules, or failure costs. A short repository trial gives better evidence than attaching a current model name to an older review result. ## How Do BugBot and Copilot Handle Findings and Feedback? BugBot reports suspected bugs and can learn from review feedback; Copilot posts review comments and suggested changes, but can repeat earlier findings on a rerun. BugBot’s effort control lets a team spend more compute on selected reviews, and Cursor says high-effort runs find more bugs in its internal tests. That is Cursor's result. Check how many real bugs BugBot finds in your own code. Cursor's learned-rules system watches reactions, replies, and human review comments for repeated signals. Candidate rules can be promoted or disabled as evidence changes. That mechanism gives a team a review product that can retain preferences without turning every dismissal into another hand-written rule. Copilot Lite effort targets common defects, security problems, and style inconsistencies. Balanced effort performs deeper analysis than Lite. Copilot also groups similar comments and adds severity labels in GitHub's newer pull-request experience. The feedback loop has a sharp limit. GitHub warns that a re-review may repeat a resolved or downvoted comment. Replies remain visible to human reviewers, while Copilot does not read or answer them in that thread. A team should track repeated noise during the pilot instead of assuming a thumbs-down trains the next review. Both products offer quick repair actions, and neither action proves the finding. Require a failing test, a reachable code path, or a broken contract before accepting generated code. The fix link saves a few clicks. You still need to check that the bug is real. ## Can BugBot and Copilot Control Pull-Request Merges? Copilot normally submits comments, but its opt-in approval preview can count towards required approvals when enabled by policy. BugBot also reports findings, but eligible organisations can use its check in branch protection and configure it to fail on unresolved issues. A check that blocks merging is different from a human approval. BugBot supports Origin, GitHub including Enterprise Server, GitLab, Bitbucket and Azure DevOps. Its automatic, once-per-pull-request and mention-only settings control when review runs and spends usage. Copilot automatic review policy lives inside GitHub rulesets. Owners can request a review when a pull request opens, when a draft becomes open, while it remains a draft, or after each new push. Individual Pro and Pro+ accounts can also enable automatic review for their own pull requests. Copilot normally submits a Comment review, which does not satisfy required approvals. Its opt-in approval preview can count towards approvals when policy allows it. Keep the human approval requirements your team needs. Copilot now covers a broader list of review surfaces. GitHub documents GitHub.com, GitHub CLI, GitHub Mobile, several editors, and Azure DevOps in public preview. BugBot makes it easy to take a pull-request finding into Cursor for a fix. Check both Git-host support and where you want to make those fixes. ## What Happens When BugBot or Copilot Review Fails? BugBot failures require checking the Cursor connection and review run; Copilot may return a reduced review when its agentic runner fails. A reduced or failed run should not be treated as a completed full-context inspection. A spend limit can stop usage-based review even while the repository integration remains healthy. The owner therefore needs access to Cursor billing and review analytics as well as the pull request. A developer may see no review without knowing that the spending limit stopped it. Copilot's richer review path crosses GitHub rulesets, AI Credit budgets, Actions runners, setup steps, and firewall policy. A reduced review can still arrive after the agentic path fails. Platform owners need a way to tell whether the comment used full project gathering before they judge a missed dependency. Put one person in charge while you try the reviewer. That person records blocked runs, reduced-context reviews, and the setting that caused each result. People need to tell a failed or limited review apart from one that found no bugs. ## How Much Do BugBot and Copilot Reviews Cost? BugBot charges review usage under the account’s Cursor plan; Copilot uses AI credits and can also incur GitHub Actions runner charges. Neither price is a fixed cost per pull request. Copilot code review comes with paid Copilot plans, then uses GitHub AI Credits for model work. Its agentic project gathering and tools also use GitHub Actions minutes. GitHub measures one AI Credit at $0.01, while the number of credits for a review depends on model and token use. The main billing difference is what each service charges for. BugBot charges usage against the account's credits or extra spending. The May average is historical, not a fixed current rate. Copilot draws from a plan's shared AI Credit allowance and can add runner minutes. Higher effort and repeated automatic runs raise consumption on both sides. How review usage is charged. | Dimension | BugBot | Copilot review | | Billing unit | Usage per run | AI credits and minutes | | Published rate | Variable; check account usage | $0.01 per credit | | Source of funds | Included usage or on-demand | Paid plan allowance | | Cost drivers | Pull-request size, effort | Model, tokens, runner time | For full allowances and restrictions, see the [BugBot pricing details](/ai-code-review/bugbot/#pricing) and [Copilot billing details](/ai-code-review/github-copilot/#copilot-review-pricing). For this choice, count review events, effort levels, and runner time across one normal month. ## Run a Fair Pilot A fair test gives each reviewer the context path it claims to use. Pick one repository with repository instructions and a working test suite. Use four pull requests from ordinary work, including one self-contained defect and one change whose consumer sits outside the edited file. Choose pull requests written by more than one developer so billing attribution and author-specific automatic review settings appear in the results. Include one bot-authored maintenance pull request as well. GitHub and Cursor can attach usage to different actors, and that difference affects who owns a failed or blocked review. 1. Run each product manually on the same pull-request head. 2. Record BugBot's effort and Copilot's Lite or Balanced setting; do not assume both defaults represent the same cost. 3. Record confirmed defects, repeated comments, missed seeded cases, and repair time. 4. Repeat the harder pull request with higher effort enabled. 5. Record BugBot usage, Copilot AI Credits, and Actions minutes. Test Copilot's fallback on a disposable branch by making the agentic runner unavailable through an approved repository setting. Confirm that a review still arrives and note which project-context evidence disappears. Restore the runner before the next production review. Test BugBot's repository guidance with one narrow `BUGBOT.md` rule tied to a known contract. A useful rule might require every tenant-scoped write to preserve the authenticated tenant identifier. Check whether the finding names the violated path and whether a later safe change stays quiet. Avoid turning the pilot into an accuracy contest built from toy syntax errors. Compare real bugs caught, repeated comments, setup work, ease of fixing bugs and cost. Finding the planted bug is not enough if everyday reviews waste the team's time. ## BugBot vs Copilot Verdict GitHub Copilot code review is the stronger first reviewer for a small or midsize GitHub team already operating Copilot runners, instructions, and budgets. Its 2026 updates let it inspect more project context, and teams can configure it through existing GitHub settings. Choose BugBot for a small team already using Cursor if its learned rules and repair links save enough time to justify another bill. Copilot keeps review inside GitHub, while BugBot connects findings directly to fixes in Cursor. ## Frequently Asked Questions ### Can BugBot and Copilot review pull requests automatically? Yes, BugBot can run on pull-request updates, while GitHub rulesets can request Copilot review when a pull request opens and after selected later events. Each extra run consumes usage, so teams should begin with one automatic review and add push-based re-reviews only after measuring repeated findings and cost. ### Can BugBot or Copilot approve a pull request? Copilot can count towards required approvals when its opt-in approval preview is enabled; its usual Comment review does not. BugBot reports findings and suggested fixes rather than providing human approval. Keep the human approval requirements your team needs. ### Does Copilot review code outside the diff? Yes, Copilot code review can inspect related files outside the diff through its agentic full-project context workflow. That path uses a GitHub Actions runner and falls back to a more limited review when the runner is unavailable. The pull request remains the subject, while related repository files can inform the comments. ### How should a team compare BugBot and Copilot review costs? Compare BugBot and Copilot costs by counting review runs, not just pull requests. Include automatic runs after new pushes, manual re-reviews, higher-effort passes, GitHub AI Credits, and GitHub Actions minutes. BugBot's average per-run figure and Copilot's included allowances are starting points; a month of real repository usage gives the useful comparison. --- ## Bugbot vs Greptile: Which AI Reviewer Fits Your Team? URL: /ai-code-review/comparisons/bugbot-vs-greptile/ Cluster: AI Code Review > Comparisons Choose BugBot if your team works in Cursor and wants a short route from a reported bug to an agent-assisted fix. Choose Greptile if cross-file problems are the bigger concern and you want a dedicated reviewer that can hand fixes to several coding agents. Greptile also offers a self-hosted Enterprise option. When [choosing a review tool](/ai-code-review/comparisons/), consider how it fits your work. We have not measured which catches more bugs on the same code. BugBot uses changed code, rules and connected context; Greptile indexes repository relationships. The [AI code review guide](/ai-code-review/) covers other options if neither fits. The [BugBot tool page](/ai-code-review/bugbot/) covers triggers, nested rules, effort, and spend controls. The [Greptile tool page](/ai-code-review/greptile/) covers graph testing, configuration, feedback, and agent handoffs. ## BugBot and Greptile in One View Product details come from [Cursor's BugBot documentation](https://docs.cursor.com/bugbot), [Greptile's product overview](https://www.greptile.com/docs/introduction), and both vendors' current pricing pages. Public product terms can change. | Dimension | Cursor BugBot | Greptile | | Context base | Diff, rules, and tools | Persistent codebase graph | | First setup | Connect and enable | Connect, index, configure | | Manual trigger | `bugbot run` | `@greptileai` | | Local guidance | `BUGBOT.md` | `.greptile/` files | | Feedback memory | Learned rules | Reaction-based learning | | Fix destination | Cursor or web | Choice of coding agent | | Billing shape | Usage per run | Seat plus credits | | Free entry | Account trial or usage | Starter and OSS paths | ## How Do BugBot and Greptile Understand Your Code? BugBot analyses pull-request changes with repository rules and connected context; Greptile uses an indexed graph of functions, classes and dependencies. BugBot reads project guidance from `.cursor/BUGBOT.md`. The root file always applies, while more-specific files join the review when changed files sit under their directories. This makes a rule's scope visible in the repository and lets an API folder carry checks that would be noise in a frontend package. Cursor also lets Teams and Enterprise accounts connect MCP servers for review context. Learned rules add another layer by turning repeated pull-request feedback into candidate guidance. BugBot therefore has more context than a bare diff, though its public docs frame changed code as the review's starting point. Greptile makes a broader claim about repository structure. It indexes functions, classes, and dependencies into a repository graph, then uses that graph when a pull request changes one part of the system. The review can refer to related code outside the diff because those relationships existed in its index before the pull request opened. Greptile also needs time to build and update its index. Greptile's quickstart says very large repositories can take one or two hours to index, while ordinary review comments usually arrive in about three minutes after setup. A large reorganisation or a stale index can affect the context a reviewer expects to be current. Greptile's `.greptile/` configuration can add instructions, scoped rules, referenced files, ignored paths, and related repositories. Cross-repository context is useful for a shared types package or SDK whose contract lives outside the application repository. It also gives Greptile access to more code. Connect only the repositories it needs. BugBot loads guidance for a review; Greptile also keeps an index of code relationships. BugBot can receive precise rules and external tool context for the changed area. Greptile maintains a graph designed to expose relationships before anyone writes a rule for the specific caller. ## What Extra Context Can BugBot and Greptile Use? BugBot can use repository rules and MCP servers; Greptile can use indexed repositories, linked codebases and referenced files. A security rule might require every database write to check the user's permissions. An MCP connection can supply issue or service data. Connect a source only when the review needs it. Broader access can cost more and expose more data. Greptile expands through indexed repositories and files referenced by `.greptile/` configuration. A service can name a shared schema repository or an SDK that defines its public types. The graph can then connect code across that boundary during review. Give either reviewer only the repositories and external systems it needs. Before connecting sensitive data, check who can change those connections and what the vendor retains after access is revoked. ## Test a Contract Break A cross-file contract break gives both products a fair, useful test. Choose a shared function whose result is consumed outside its own file. A permission helper, API response type, event payload, or database mapper works well because the caller has a clear expectation that can fail. Create a small pull request that changes the producer while leaving one consumer untouched. Keep the failing relationship out of the pull-request description. The reviewer should find the link from code and repository context rather than a hint written for the experiment. 1. Name the producer and hidden consumer in a private scorecard. 2. Run BugBot and Greptile on the same commit. 3. Record whether each comment names the affected consumer. 4. Require a reachable failure or a testable contract. 5. Repeat with a compatible change that should stay quiet. Test a safe change too. A reviewer that flags every shared-function edit can appear thorough while moving the investigation burden onto humans. A useful reviewer catches the broken call and leaves the safe change alone. Run a second pass after adding one narrow repository rule to each product. Put the BugBot rule in the closest applicable `BUGBOT.md`. Put the Greptile rule in a scoped `.greptile/rules.md` or structured configuration entry. The wording should describe the contract without naming the planted bug. This shows whether the reviewer found the problem itself or needed a rule to point it there. A product may miss the relationship unaided and catch it after guidance, which still has worth. Record whether the reviewer needed the extra rule to find the bug. ## Can BugBot and Greptile Learn Your Review Standards? Yes, BugBot and Greptile can use explicit rules and feedback to adapt reviews to your team. BugBot's repository rules follow the directory tree. A security rule for the whole project can sit at the root, while an API package carries a stricter rule for authentication or transaction handling. Reviewers can inspect the same file that guided the bot, which makes a questionable comment easier to trace. Cursor's learned-rules feature watches reactions, replies, and human review comments. Repeated signals can become candidate rules, then lose status if later evidence stops backing them. Teams should review promoted rules because an old convention can become a reliable source of stale comments after the architecture moves. Greptile offers dashboard controls for strictness and comment types, plus repository configuration for rules and ignored paths. Its quickstart lists Syntax, Logic, and Style controls. Teams can also filter reviews by labels, authors, branches, and pull-request text, which helps keep generated or low-risk changes out of the automatic queue. Greptile says reactions and replies teach the reviewer what the team cares about, with unwanted feedback declining over time. Check that claim during your trial. Save two or three dismissed patterns, then submit equivalent safe changes later and check whether the comments return. Unhelpful comments waste people's time. Unnecessary reruns waste money. Adjust the rules to reduce the comments, and the triggers to reduce the reruns. ## When Do BugBot and Greptile Run Reviews? BugBot can review updates automatically or wait for a manual request; Greptile can also run automatically or on request and filter reviews by branch, label, author and pull-request text. Those controls decide whether the reviewer meets a stable change or comments while the author is still rewriting it. Cursor's automatic update mode fits teams that want a fresh pass after each meaningful push. The same mode can spend several review runs on one pull request. Mention-only or once-per-pull-request settings keep the queue smaller during calibration. Greptile skips drafts by default and says a normal review arrives in about three minutes after indexing. A team can reserve automatic review for a ready label, then use `@greptileai` for an older or unusually risky change. That gives Greptile a finished change to review. Measure how long it takes from marking a change ready to confirming a useful finding. Fast comments help little if they take longer to dismiss than the review saves. ## How Do BugBot and Greptile Help Fix Bugs? BugBot sends confirmed findings into Cursor or its web agent; Greptile offers a choice of coding agents through Fix with your Agent. A finding can open through "Fix in Cursor" or move to Cursor's web agent. Cursor also documents Fix All and Autofix behaviour, though the [BugBot repair guide](/ai-code-review/bugbot/#fix-links) covers the safety checks in more detail. That handoff removes the search for the affected line and brings the finding into the editor where a Cursor user already works. The proposed code still needs inspection and tests. A fast handoff can make a weak finding more tempting, so the team should require evidence before accepting the repair. Greptile's "Fix with your Agent" action is built around choice. Its current overview lists Claude Code, Codex, Conductor, Cursor, and Devin. The handoff includes the issue, file paths, line numbers, and suggested code, giving the chosen agent a compact repair brief. Agent choice helps a team that already splits work across coding tools. It also adds variation to the repair environment, permissions, and output. Whichever agent receives the comment, inspect the diff, run the relevant tests and preserve human approval. BugBot keeps review and repair in Cursor. Greptile lets its review feed a separate coding agent. Teams with one standard editor may prefer the first arrangement; teams with several approved agents can use the second without forcing a new repair tool. ## Which Git Hosts Do BugBot and Greptile Support? BugBot supports GitHub, GitLab, Bitbucket, Azure DevOps and Origin; Greptile supports GitHub and GitLab, including documented self-hosted Git installations. Repositories must be enabled for indexing, and the product needs enough access to build its graph. Connect only the repositories Greptile needs to review the change and its dependencies. Greptile's Enterprise plan lists self-hosting, SSO/SAML, GitHub Enterprise coverage, custom terms, and a data-processing agreement. Public plan text cannot answer a team's retention or residency questions. Request the current architecture and contract details before connecting regulated or customer-sensitive code. BugBot's current setup documentation includes GitHub Enterprise Server, GitLab Self-Hosted and Bitbucket Data Center. Confirm provider-specific features as well as access, because some Autofix modes are not available on every host. BugBot can use MCP servers on Teams and Enterprise plans. That can pull issue or service context into a review, and it creates another outbound access path to approve. For Greptile, also check which other repositories the reviewer can read. For either product, start with one repository and the smallest set of external systems. Record who can enable a repository, who can change review rules, and which agent can write fixes. Good findings do not justify giving a reviewer access to code it does not need. ## How Much Do BugBot and Greptile Cost? BugBot bills for usage, while Greptile Pro combines a monthly seat price with credits. Cursor estimated $1.00 to $1.50 per BugBot run in May 2026, but that historical average is not a current tariff. Check the account dashboard for allowances, effort settings and actual run costs. Greptile Pro costs $30 per seat each month and includes 50 credits per seat. Base reviews use one credit, Plus reviews use three and Apex reviews use ten; extra credits cost $1 each. The free Starter plan includes 50 monthly credits for one active developer. Published plan terms. | Dimension | BugBot | Greptile | | Billing unit | Usage per run | Seat plus credits | | Paid floor | Variable; check account usage | $30 per seat monthly | | Included pool | Account credits and extra usage | 50 credits per seat | | Overage | On-demand spend | $1 per extra credit | Greptile's monthly price includes review credits and its codebase graph service. BugBot's cost depends on how many reviews you run and how much work they do. A solo developer should check Greptile Starter before paying for Pro. For a team, compare actual monthly reviews and reruns, including higher-cost Plus or Apex reviews. A vendor's average run price is a poor basis for a precise break-even calculation. Use the full [BugBot pricing section](/ai-code-review/bugbot/#pricing) and [Greptile pricing section](/ai-code-review/greptile/#pricing) for each product's current terms. Compare the bugs Greptile's codebase graph helps catch with the time BugBot saves when fixing them in Cursor. ## My Verdict Greptile is the stronger single purchase for a midsize or large team adding a dedicated reviewer to find cross-file contract breaks. The persistent graph gives the product a distinct job beside ordinary pull-request review, while its agent handoff keeps repair-tool choice open. BugBot wins for a small Cursor-standardised team that prizes the shortest path from comment to edited code and wants spend to follow review volume. Greptile offers a codebase graph; BugBot keeps fixes close to the Cursor editor and charges by usage. ## Frequently Asked Questions ### Do BugBot and Greptile work with GitLab? Yes, BugBot and Greptile both support GitLab reviews. Greptile's quickstart documents its GitLab token and webhook setup, while Cursor's product changelog lists BugBot on GitLab and GitHub Enterprise Server. Confirm self-hosted requirements, plan eligibility, and repository permissions with each vendor before connecting production code. ### How do you trigger BugBot and Greptile manually? Trigger BugBot with cursor review or bugbot run in a pull-request comment. Greptile documents tagging @greptileai on a GitHub or GitLab pull request, including prompts that ask for a focused check. Manual triggers are useful during a pilot because each review event can be measured. ### Do BugBot and Greptile learn from feedback? Yes, BugBot and Greptile both learn from review feedback. BugBot turns repeated pull-request signals into candidate learned rules that can be promoted or disabled. Greptile says reactions and replies teach the reviewer what the team cares about over time. Treat both as product claims and test repeated cases. ### Is Greptile free for open-source projects? Greptile's pricing page says qualified non-commercial projects with an OSI-approved licence can apply for free service. The same page lists a free Starter plan with one active developer and 50 monthly credits. Eligibility and credit terms may change later. Facts and prices were checked against the official vendor pages on August 8, 2026. --- ## Claude Code vs CodeRabbit: AI Code Review Compared URL: /ai-code-review/comparisons/claude-code-vs-coderabbit/ Cluster: AI Code Review > Comparisons CodeRabbit is my starting choice for routine pull-request review because it offers detailed configuration, several Git hosts and pricing by developer seat. Managed Claude Code Review is worth a trial for changes where its multi-agent verification and adjustable depth could justify a separate usage charge. Neither product has to replace the other, but paying for two reviewers should buy more than duplicate comments. Claude Code is our daily coding environment, and CodeRabbit runs in our promotion review flow. That experience is separate from testing Anthropic’s managed review service. My [AI code review guide](/ai-code-review/) covers the broader shortlist. ## At a Glance How each review runs. | Comparison criterion | Claude Code Review | CodeRabbit | | Pipeline | Multi-agent with verification | Configurable review and tools | | Severity | Important, Nit, Pre-existing | Profile-controlled feedback | | Review depth | Quick, Standard or Deep review | No duration commitment | | Failed run | Neutral; retry depends on failure | Quota and status signals | Claude Code Review reads `REVIEW.md` and `CLAUDE.md`; its manual trigger is `@claude review`. CodeRabbit uses `.coderabbit.yaml` and `@coderabbitai review`. What each one costs and where it runs. | Comparison criterion | Claude Code Review | CodeRabbit | | Review price | Variable token-based usage | $24/dev/mo annual, $30/dev/mo monthly | | Higher tier | Enterprise plan eligible | Team $48, Advanced $72 per developer/month, billed annually | | Git providers | Managed service on GitHub | Four documented providers | | Private deployment | Team-owned Action alternative | Enterprise self-hosting option | Cost and review frequency help narrow the choice. CodeRabbit can cover a routine stream under a seat plan. Managed Claude Code Review bills separately for each run, with the selected review level affecting the cost. ## Which Claude and CodeRabbit Review Options Are Compared? “Claude Code review” covers several workflows. A developer can run `/code-review` locally, a team can build a workflow with Claude Code Action, or an organisation can buy Anthropic's managed GitHub App. This head-to-head compares the managed service with CodeRabbit's pull-request reviewer. The local Claude command is private to the developer's session and follows `CLAUDE.md`. Claude Code Action runs on a team-owned GitHub Actions runner, uses the model provider the team configures, and needs a workflow that decides what to post. Neither path proves how Anthropic's managed multi-agent service performs. CodeRabbit also spans pull requests, the command line, and editor extensions. Its hosted pull-request product has a seat plan, review limits, and organisation configuration. The [CodeRabbit guide](/ai-code-review/coderabbit/) explains how to set up each option. Compare the same kind of review when choosing a product. Managed Claude against hosted CodeRabbit answers the purchasing question. Local Claude against CodeRabbit CLI answers a pre-push workflow question, while Claude Code Action against a hosted bot compares team-owned automation with a managed service. The services have separate bills. Daily Claude Code use draws on your plan allowance or model-provider account. Managed Code Review is extra usage, and CodeRabbit pull-request review comes from its paid author seats and rate limits. ## What Do Claude Code Review and CodeRabbit Check? Claude Code Review looks for correctness problems in a pull request and its surrounding code, while CodeRabbit combines configurable AI review with integrated analysis tools. Claude verifies candidate findings before posting them. Its Quick, Standard and Deep levels trade analysis time and cost; Standard is the default. CodeRabbit documents configurable AI review alongside linters and security tools. Profiles change the amount of feedback, and YAML settings control paths, summaries, tools, and other review behaviour. Its configuration can target a broader set of repository concerns than Anthropic's documented default correctness focus. Those descriptions do not establish which reviewer catches a particular cross-file defect. Test a change that breaks a rule elsewhere in your code. Check whether each reviewer names the affected code and explains what fails. Use the same definitions of a confirmed defect and false alarm when [comparing the results](/ai-code-review/comparisons/). ## Pricing Managed Claude Code Review is available to Team and Enterprise organisations and charges token-based usage credits separately from the plan’s included usage. There is no flat per-review price. The review level, change size, surrounding code and verification work affect cost. See Anthropic’s [review billing documentation](https://code.claude.com/docs/en/code-review-costs) for the current terms. Trigger choice directly multiplies that usage charge. Once-after-creation runs once, every-push runs after each push, and manual mode waits for `@claude review`. Anthropic provides a monthly spend cap and repository-level cost reporting. CodeRabbit Essentials costs $24 per developer each month with annual billing or $30 month to month, according to its plans page. CodeRabbit Team costs $48 per developer per month when billed annually or $60 month to month. Advanced costs $72 or $90 on the same respective billing schedules. The vendor assigns paid seats to developers who create pull requests and publishes review limits for each plan. CodeRabbit Free allows unlimited public and private repositories, though private hosted pull-request review becomes summarisation-only after the 14-day Advanced trial. Code review remains available through the IDE and CLI. Qualifying public open-source projects receive Team features through a separate OSS plan. Published plan terms. | Dimension | Claude Code Review | CodeRabbit | | Billing unit | Usage credits per review | Paid developer seats | | Published price | Variable token-based usage | $24/dev/mo annual, $30/dev/mo monthly | | Higher tier | Quick, Standard and Deep levels | Team $48, Advanced $72 per developer/month, billed annually | | Free entry | Local review uses its own plan or provider allowance | Summaries, IDE, CLI | Compare Claude’s actual review spend with CodeRabbit’s author seats, review limits and optional overages. CodeRabbit’s Agent and security features can have separate usage charges. Neither bill is accurately described as unlimited review for one fixed fee. ## How Do You Configure Claude Code Review and CodeRabbit? Claude Code Review uses `REVIEW.md` for review guidance and `CLAUDE.md` for project context; CodeRabbit uses `.coderabbit.yaml` plus organisation settings. Managed Claude reads review guidance from the base or default branch rather than the pull request’s edits. CodeRabbit can read feature-branch configuration, so check shared overrides and the resolved settings shown in its walkthrough. CodeRabbit uses `.coderabbit.yaml`, and the current configuration reference offers `quiet`, `chill`, and `assertive` profiles with `chill` as the default. The same schema places `poem` under `reviews` and defaults it to `false`. Write rules that describe something you can check in the code. For example, require every database write to check the user's organisation, and ask the reviewer to name any query that fails that check. A generic instruction to "check security" cannot define the safe path or help a reviewer dismiss a false lead. ## What Happens When Claude or CodeRabbit Cannot Review? Claude Code Review can finish neutral or skipped when it cannot complete a review; CodeRabbit reports limits and status in its walkthrough. A missing review is not a clean result. Claude retries an interrupted session once automatically. Other failures need `@claude review` or GitHub’s Re-run button where supported; Re-run is unavailable for forks. The recovery path is a top-level `@claude review once` comment, or a new push when the pull request is already subscribed. A reached spend cap creates a separate skipped-review comment. In both cases, “neutral” means no merge block, not a clean code result. CodeRabbit exposes rate-limit status in its walkthrough and accepts a command that reports remaining reviews. File limits, provider permissions, and configuration access can also narrow the intended run. During the pilot, save the status that accompanies each review. Test one visible review failure for each managed service. Remove a test repository permission or use a low trial budget where safe, then verify that the author and release reviewer can see the missing review. Assign an owner for recovery before either service becomes automatic. Count how many requested reviews actually finish. A tool that finds good bugs but skips half the pull requests is hard to rely on. ## Where Do Claude Code Review and CodeRabbit Process Code? Claude Code Review runs on Anthropic infrastructure; CodeRabbit offers hosted review and an Enterprise self-hosting option. Managed Claude review is unavailable with Zero Data Retention, HIPAA configuration or customer-managed encryption keys. An organisation admin selects repositories and controls the review spend cap. Those product facts can rule out the managed path before a quality trial. Claude Code Action is the documented alternative when a team needs its own runner and provider route. It can call Anthropic, Amazon Bedrock, Google Vertex AI, or Microsoft Foundry. The team then owns tokens, workflow permissions, network access, prompt scope, and output publishing. CodeRabbit lists self-hosting, custom access controls, audit logging, and service commitments as Enterprise options. Ask the vendor where the service runs and how it connects to your Git host. An Enterprise label alone does not answer where source code or findings travel. Decide separately whether a tool can read code or change it. Hosted review needs source access and permission to post findings. Autofix or an agent handoff may need branch-write access, which you should grant only after checking the quality of its findings. Ask both vendors for the data path, retention terms, subprocessors, region choices, audit events, and deletion behaviour tied to the purchased plan. Record the answer beside the plan and deployment, not as a general product badge. ## Can Claude Code Review and CodeRabbit Help Fix Findings? CodeRabbit can discuss findings and help prepare fixes; managed Claude Code Review does not answer inline replies or push code changes. The documented repair loop is to fix the code and push. A later push-triggered run can auto-resolve the thread when the issue is gone. CodeRabbit provides more interaction around its comments, including autofix and prompts for coding agents. That can shorten the path from finding to patch. It also makes it easier to act before proving that the comment describes a reachable failure. For either reviewer, check the finding before accepting a fix: 1. Confirm the path and violated requirement. 2. Add or identify a test that proves it. 3. Assign the repair to one author or agent. 4. Review the patch and run the relevant checks. 5. Rerun review on the new head when needed. Do not ask both services to patch the same defect during the trial. That would test their coding ability as well as their reviews. First judge whether the finding gives a person enough evidence to act. ## Should You Use Claude Code Review Alongside CodeRabbit? Using Claude Code Review alongside CodeRabbit is worthwhile only if Claude adds useful findings that justify its cost and delay. Test that on recent pull requests with known outcomes, including authentication changes, migrations and cross-service contracts. Keep CodeRabbit’s routine configuration stable and add one Claude review to the same commit. 1. Freeze CodeRabbit's profile, tools, and trigger. 2. Use `@claude review once` on the same ready commit. 3. Record rules, head commit, time, cost, and run status. 4. Label distinct confirmed defects, repeats, wrong findings, and misses. 5. Make one documented rules adjustment for each product. 6. Repeat the same known cases and compare the results. Keep the existing human review and test gates unchanged. Human reviewers and tests help catch issues the bots miss. A neutral Claude check or a CodeRabbit summary is never a human approval. Count the real bugs Claude found that CodeRabbit and the usual human review missed. Put that beside the extra minutes and usage charge. Keep a second reviewer only if it also catches extra bugs in everyday work. Decide when each reviewer should run. Routine review may stay with CodeRabbit, while someone requests Claude for changes that need another look. If the second pass rarely catches anything useful, remove it. ## The Claude Code and CodeRabbit Verdict Choose CodeRabbit for the routine review stream when its seat price, configurable scope, and Git-provider coverage fit the team. Trial managed Claude Code Review where its verification and configurable depth might justify the extra usage cost and wait. Keep both only if the trial shows that the second catches bugs you would otherwise merge. ## Frequently Asked Questions ### How does Claude Code Review charge? Anthropic bills managed Claude Code Review separately through usage credits. Charges depend on token usage, pull-request size and the selected Quick, Standard or Deep review level. ### Can CodeRabbit post fewer review comments? CodeRabbit can post fewer review comments when teams select a quieter profile, exclude irrelevant paths or disable redundant checks. CodeRabbit provides quiet, chill, and assertive review profiles, with chill as the current default. Its YAML configuration also controls tools, summaries, path instructions, and review behaviour. ### Does Claude Code Review reply to comments? Managed Claude Code Review does not respond to inline replies or update the pull request in response to them. A fix and push can resolve a thread when push-triggered review is active. ## Read the Full Guides - [Claude Code review guide](/ai-code-review/claude-code/) for managed and local reviews. - [CodeRabbit guide](/ai-code-review/coderabbit/) for profiles, tools, and plan limits. - [AI code review comparisons hub](/ai-code-review/comparisons/) for the shared evaluation method. --- ## CodeRabbit vs Cursor BugBot: Which AI Reviewer to Pick URL: /ai-code-review/comparisons/coderabbit-vs-bugbot/ Cluster: AI Code Review > Comparisons Choose CodeRabbit for configurable pull-request reviews, summaries and integrated checks. Choose BugBot if the short route from a reported bug to a fix in Cursor matters more to your team. Both support the main Git hosts, so this is no longer a choice between broad platform support and GitHub alone. My preference is CodeRabbit for this type of review workflow. That is an opinion based on our use, not proof that it will find more bugs in your repository. The broader [AI code review tools guide](/ai-code-review/) covers the alternatives. I have used CodeRabbit on our projects, and I paused BugBot over Cursor usage limits, so my firsthand observations cover CodeRabbit and that pause. The detailed setup, rule files, and finding-triage methods already live in the [CodeRabbit guide](/ai-code-review/coderabbit/) and [BugBot guide](/ai-code-review/bugbot/). ## The Matchup at a Glance The tools organise review differently. CodeRabbit combines AI comments with configurable linters and security tools. BugBot analyses pull-request diffs, explains suspected issues, and provides handoffs into Cursor or Cursor's web agent. Product behaviour and prices come from [CodeRabbit's documentation](https://docs.coderabbit.ai/) and [Cursor's BugBot documentation](https://docs.cursor.com/bugbot). | Dimension | CodeRabbit | Cursor BugBot | | Review shape | AI plus configured tools | Diff findings and fixes | | Manual trigger | `@coderabbitai review` | `bugbot run` | | Repository rules | `.coderabbit.yaml` | `.cursor/BUGBOT.md` | | Fix handoff | Pull-request fixes | Cursor or web agent | | Git hosts | Four documented hosts | GitHub, GitLab, Bitbucket and Azure DevOps | | Billing basis | Developer seats and limits | Usage per review run | | Published price | $24/dev/mo annual, $30/dev/mo monthly | Usage-based; see account pricing | | Free access | Summary, CLI, and IDE | 14-day trial | | OSS access | Qualified Team features | No published OSS plan | A team with several existing scanners may value BugBot's direct issue-to-agent path. CodeRabbit may suit a team that wants review rules and integrated checks in one place. ## How Do CodeRabbit and BugBot Review Code? CodeRabbit combines pull-request summaries and inline findings with configurable analysis tools. BugBot reviews changed code, explains suspected defects and offers a direct handoff to a coding agent. Both can run automatically or on request, and both support pull-request summaries. CodeRabbit's `Chill` and `Assertive` profiles change the default strictness. A repository can then enable or disable individual tools in `.coderabbit.yaml`, as described in the [official CodeRabbit tools documentation](https://docs.coderabbit.ai/tools). BugBot can review every pull-request update or wait for a manual comment. Cursor documents `cursor review`, `bugbot run` as manual triggers. Personal settings can limit it to mention-only runs or one run per pull request. A BugBot comment can open through Fix in Cursor or Fix in Web. That short handoff is useful when the author has confirmed the finding and wants an agent to prepare a patch. It also creates a risk of moving from a plausible comment to generated code before anyone proves the failure. Start with manual reviews if you want to learn what each tool catches before enabling automatic runs. CodeRabbit uses `@coderabbitai review`; BugBot accepts `cursor review` or `bugbot run`. Choose enabled repositories carefully. BugBot can review all contributors on enabled team repositories, not just Cursor team members. ## How Do You Configure CodeRabbit and BugBot? Configure CodeRabbit through `.coderabbit.yaml` and BugBot's review instructions through `.cursor/BUGBOT.md`. BugBot also reads `.cursor/config/bugbot.yaml` from the pull request's base branch for repository settings. Review guidance tells the agent what to check; settings control how it runs. [Cursor's official guide](https://docs.cursor.com/bugbot) documents that rule-loading behaviour. To test it, give both tools the same repository rule, then open a pull request that violates it. ``` {`# Review invariant used in both tools - Trace tenant ownership before every database write. - Ignore generated clients under src/generated/. - Require a failing test for a changed public contract.`} ``` For CodeRabbit, write the rule as a path-specific instruction and enable only relevant tools. For BugBot, place the shared rule at the root and add a nested file only where a directory has different review requirements. Keep the wording close enough that the product, rather than the prompt, explains any result difference. Check whether each reviewer names the changed line, the code it affects and what could go wrong. Comment length has little value on its own. A short false alarm still wastes time. A longer comment can be useful if it explains a real bug and the test needed to catch it. ## Do CodeRabbit or BugBot Produce More Useful Findings? I cannot name a universal winner for useful findings. CodeRabbit's profiles, tool settings and path instructions let teams reduce irrelevant comments; BugBot's rules and effort settings also affect what it reports. Compare confirmed bugs and time spent checking false alarms, not comment counts. BugBot puts its repair links beside the finding. That convenience suits a team whose authors already work in Cursor. It can also encourage a fast accept-and-rerun loop, so confirm how the bug occurs or write a test that reproduces it before requesting a fix. Use the same definitions of a confirmed defect, duplicate and false alarm when [comparing the reviewers](/ai-code-review/comparisons/). After twenty representative pull requests, compare the real bugs caught with the time authors and reviewers spent checking comments. Inspect duplicate findings across both comment streams as well. Two bots can each look accurate in isolation while creating an expensive review queue by reporting the same issue in different words. ## How Does BugBot Handle Repeat Reviews? BugBot reviews changes since the previous review by default and can avoid charging twice for a local and remote review of the same patch. Its current effort settings are Low, Default, High and Smart; Smart chooses effort from your instructions. Record the setting when comparing cost or findings. Local `/review-bugbot` and remote pull-request reviews are related workflows, not evidence that every rerun is free. Check whether the diff changed and whether local review is supported in your Cursor version. The [BugBot guide](/ai-code-review/bugbot/) explains the current setup. ## How Much Do CodeRabbit and BugBot Cost? CodeRabbit starts at $24 per developer per month billed annually, or $30 month to month, for Essentials. BugBot uses review-based charges under the applicable Cursor account terms; size, effort and reruns affect the bill. [CodeRabbit lists Essentials at $24 per developer per month on annual billing](https://docs.coderabbit.ai/management/plans) and $30 month to month. Team costs $48 per developer per month billed annually or $60 month to month; Advanced costs $72 or $90 per developer per month on the same respective terms. Enterprise pricing is custom. Optional usage overages and separately billed agents or security scans can add to the seat price. Free provides pull-request summaries plus IDE and CLI review after the trial. Qualifying public open-source projects receive Team features through a separate OSS plan. Published plan terms. | Dimension | CodeRabbit | BugBot | | Billing unit | Paid developer seats | Usage per run | | Paid floor | $24/dev/mo annual, $30/dev/mo monthly | $1 to $1.50/run in the May 2026 estimate | | Higher tier | Team $48/dev/month; Advanced $72/dev/month, billed annually | Higher effort settings | | Legacy terms | Plan and contract dependent | $40/seat/mo until renewal | Costs depend on how many people write code and how often they request reviews. CodeRabbit charges review seats to developers and publishes per-developer hourly limits. BugBot turns each run into usage, so trigger frequency, review effort, and pull-request size all affect spend. A team with a steady group of authors can forecast CodeRabbit from seats and peak hourly demand. Estimate BugBot from actual run costs in your account and the effort settings you intend to use. Use the account's live billing screen when an older Cursor contract is still in transition. Use the [CodeRabbit pricing section](/ai-code-review/coderabbit/#pricing) and [BugBot pricing section](/ai-code-review/bugbot/#pricing) for the full plan detail. ## Which Git Hosts Do CodeRabbit and BugBot Support? CodeRabbit and BugBot both support GitHub, GitLab, Bitbucket and Azure DevOps. CodeRabbit's [central configuration guide](https://docs.coderabbit.ai/configuration/central-configuration) also describes configuration repositories across those platforms. Cursor's Bugbot setup needs Cursor administrator access plus admin access on the Git host, followed by a connection and repository enablement. Its docs cover GitHub (including Enterprise Server), GitLab, Bitbucket and Azure DevOps, the last one still in public beta. Put someone in charge of deciding which repositories receive automatic review, who maintains rules, how repeated comments are suppressed, and what happens when the tool is unavailable. If nobody fixes missed runs or noisy rules, developers will stop trusting the bot. For a regulated team, add data handling, retention, audit, and deployment questions before a source-code connection. Vendor feature pages can start that conversation, but the signed terms and your security review decide it. A badge on a product page does not prove it meets your requirements. ## Run a Useful Two-Tool Pilot Use one repository and a fixed set of pull requests. Include a small contained fix, a cross-file contract change, an authorisation path, a generated-file change, and a large refactor. The mix exposes each reviewer's behaviour without turning the pilot into a benchmark contest built for one product. 1. Install both tools without enabling organisation-wide automatic review. 2. Give both the same three repository rules. 3. Trigger each reviewer once after tests are ready. 4. Classify confirmed defects, duplicates and false alarms consistently. 5. Record repair time, duplicate time, and rerun count. 6. Repeat after one configuration adjustment per tool. The second pass is important because both tools expose customisation. It shows whether a weak first review reflects the product's limits or a correctable setup gap. Stop after one round of adjustments instead of tuning indefinitely until the preferred product wins. Keep human review unchanged during the pilot. A missed issue only becomes visible when the existing reviewer still checks for it. Without the usual human review, you may never discover what the bots missed. ## What Have I Used in Practice? Our team used CodeRabbit through the explicit `@coderabbitai review` trigger in relevant promotion workflows. From that experience, I value careful configuration and triggers more than another bot comment. The team still inspects findings and checks live review-thread state. BugBot was paused on July 19, 2026 because of Cursor usage limits in this repository workflow. Cursor's BugBot pricing has changed since then. A short trial can measure whether the Cursor repair handoff saves enough effort to justify another paid reviewer. ## Use the Full Tool Guides For setup and configuration details: - [CodeRabbit guide](/ai-code-review/coderabbit/) covers setup, configuration precedence, local review, finding triage, and plan limits. - [BugBot guide](/ai-code-review/bugbot/) covers manual triggers, nested rule files, fix handoffs, spend policy, and rollout checks. - [AI code review comparisons](/ai-code-review/comparisons/) links the other direct matchups when neither product fits the platform or workflow. ## Frequently Asked Questions ### Can CodeRabbit and BugBot review the same pull request? Yes, CodeRabbit and BugBot can review the same pull request when both are enabled on an eligible repository. Give each reviewer a separate role during a pilot, then measure confirmed defects, repeated findings, review time, and comment overlap before paying for both. ### Do CodeRabbit and BugBot have free plans? CodeRabbit Free provides pull-request summaries plus IDE and CLI review after its 14-day Advanced trial. Qualifying public open-source projects receive Team features at no cost. Cursor gives new BugBot users a 14-day trial before a paid plan. ### Does BugBot require the Cursor editor? No, BugBot does not require the Cursor editor to review pull requests. It can run through the connected Git host automatically or from a comment. The editor becomes valuable when a finding uses Fix in Cursor, but reviewers can still read and triage the original pull-request comment without opening the editor. ### Which Git hosts do CodeRabbit and BugBot support? CodeRabbit documents pull-request review on GitHub, GitLab, Bitbucket, and Azure DevOps. Cursor's Bugbot documentation now lists GitHub (including Enterprise Server), GitLab, Bitbucket and Azure DevOps, so both reviewers cover the four main Git hosts. ## The CodeRabbit vs BugBot Trade-Off Choose CodeRabbit when the team needs configurable review across several Git hosts and can use its summary, IDE, CLI, or qualified OSS entry paths. Choose BugBot when your team already fixes code in Cursor and its repair links save enough time to justify the review charges. Keep both only if the extra bugs they catch justify the duplicate comments and total cost. --- ## CodeRabbit vs GitHub Copilot Code Review URL: /ai-code-review/comparisons/coderabbit-vs-github-copilot/ Cluster: AI Code Review > Comparisons CodeRabbit is my preference for a dedicated pull-request reviewer. It gives teams configurable checks, summaries and support across four Git hosts. Copilot code review is worth trying first if your team already pays for Copilot and wants review inside its existing GitHub workflow. Included access does not mean unlimited free reviews. AI credits and runner costs still matter. We have used CodeRabbit on our projects and removed mandatory Copilot review after noisy results in our workflow. Copilot has since gained broader context and controls, so that experience is not a fresh head-to-head test. Both products now look beyond the diff; compare useful findings rather than an outdated feature checklist. For the wider shortlist, see our [best AI code review tools](/ai-code-review/). The [CodeRabbit guide](/ai-code-review/coderabbit/) and [GitHub Copilot review guide](/ai-code-review/github-copilot/) cover setup, configuration, and full plan detail. ## The Matchup at a Glance Both tools can review pull requests automatically, use repository guidance, and suggest repairs. The main differences are supported Git hosts, review settings, costs and the infrastructure needed to read related code. Product behaviour and prices come from [CodeRabbit's official documentation](https://docs.coderabbit.ai/) and [GitHub's Copilot code review documentation](https://docs.github.com/en/copilot/concepts/agents/code-review). | Dimension | CodeRabbit | GitHub Copilot | | Product scope | Dedicated review platform | Wider Copilot suite | | Project context | Knowledge and linked repos | Agentic full-project gathering | | Static tools | 40-plus configured tools | Separate Code Quality layer | | Git hosts | Four documented hosts | GitHub plus Azure preview | | Usage meter | Plan review limits | AI credits and runners | | Configuration | `.coderabbit.yaml` | Instructions and skills | | Entry price | $24/dev/mo annual, $30/dev/mo monthly | Business $19/user/mo | | Higher tier | Team $48/dev/month; Advanced $72/dev/month, billed annually | Enterprise $39/user/mo | | Free access | Summary, CLI, and IDE | Paid plans only | | OSS access | Qualified Team features | Plan eligibility varies | Copilot's repository context features changed in 2026, as did CodeRabbit's plans and rate limits. Check current features and costs, then test the tools on your own repository before buying. ## Should You Choose CodeRabbit or Copilot Code Review? Choose CodeRabbit when review needs its own configuration, budget and Git-host integrations; choose Copilot when keeping review within your existing GitHub setup is the stronger priority. Judge either choice by the bugs it catches and the time its comments take to check. CodeRabbit’s CLI and editor extensions can also move review before a pull request. GitHub Copilot code review is one part of Copilot. GitHub's [official availability section](https://docs.github.com/en/copilot/concepts/agents/code-review#availability) documents it on GitHub.com, GitHub Mobile, the GitHub CLI, Visual Studio Code, Visual Studio, Xcode, JetBrains IDEs, and Azure DevOps in public preview. Copilot is convenient for teams already using GitHub. A team already managing GitHub policies, licences, Actions budgets, and repository instructions can add review without introducing a separate vendor relationship. Review then shares credits and infrastructure with other Copilot work. A blocked runner, depleted AI-credit budget, or restrictive organisation policy can affect the review path. CodeRabbit has its own rate limits and account controls instead. Those limits affect whether a review completes, not just what it says. GitHub also publishes an excluded-files list for Copilot review. Dependency manifests, lockfiles, logs, and SVG files are among the categories the current concept page says it does not review. A repository that changes dependencies frequently must keep its existing supply-chain and human-review controls outside Copilot's comment stream. CodeRabbit's configured tools can inspect several dependency and security concerns, but tool support still varies by language, file, and platform. List the files each product skipped during the pilot. Check which files were reviewed, as well as which comments appeared. ## Can CodeRabbit and Copilot Review Beyond the Diff? Yes, CodeRabbit and Copilot can both review code beyond the changed lines. CodeRabbit supports repository context and linked repositories; Copilot gathers full-project context using its agentic review workflow, which depends on runner access. GitHub also says code review can use relevant Model Context Protocol servers and agent skills when the repository or pull request gives clear signals. Availability depends on repository configuration and supported integrations. CodeRabbit approaches context through repository configuration, knowledge-base material, learnings, guideline files, and linked repositories. Its current plans limit how many linked repositories can participate in multi-repository analysis. The [official multi-repository guide](https://docs.coderabbit.ai/knowledge-base/multi-repo-analysis) documents same-platform access and plan limits. A fair context test changes a contract in one file while leaving a consumer outside the diff. Ask both reviewers to identify the affected consumer and explain the failure path. Then repeat with the context-gathering runner unavailable or the linked repository removed. Also check what happens when only part of the setup works. A reviewer that quietly falls back to a narrower view needs to make that state visible. Reviewers should know whether a clean report means no issue was found or the expected context never loaded. ## How Do You Configure CodeRabbit and Copilot Reviews? Configure CodeRabbit through `.coderabbit.yaml` and central settings; configure Copilot with supported repository instructions, skills and review policies. CodeRabbit’s central configuration repositories and global overrides can carry organisation policy. Its `Quiet`, `Chill` and `Assertive` profiles give teams a starting level before individual settings refine it. Copilot reads more than `.github/copilot-instructions.md` on supported review surfaces. GitHub's current guide says repository instructions, `AGENTS.md`, path-specific instruction files, and relevant skills can contribute. It reads these files from the pull request's head branch. A contributor may be able to change the instructions used to review their own pull request. Repository protections, review policy, and human inspection must account for the possibility that code and its reviewing instructions change together. GitHub documents this behaviour in its [code review customization guide](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/request-a-code-review/use-code-review#customizing-copilots-reviews-with-custom-instructions). CodeRabbit teams should run the same check against repository configuration changes and any organisation override. Copilot code review also uses a purpose-built mix of models, prompts, and system behaviour. GitHub says users cannot switch the review model. Choosing a model in Copilot Chat does not tell you which model reviewed a pull request. A new model launch does not prove the reviewer uses it either. CodeRabbit similarly controls the service behind its review product while exposing tool and policy configuration to the buyer. Compare the behaviour and evidence available to the team. Avoid scoring either product from an assumed model name that the vendor has not attached to the review. ``` {`# AGENTS.md review rule used in the pilot - Trace authorisation before every tenant-scoped write. - Check changed contracts against consumers outside the diff. - Cite the protecting test when dismissing a suspected failure.`} ``` Keep the shared rule in a file that does not depend on either tool when both products support it. Put tool-specific tuning in each product's configuration. That split reduces prompt differences and makes the pilot about review behaviour rather than two unrelated instruction sets. ## Where Can You Use CodeRabbit and Copilot Code Review? CodeRabbit reviews pull requests on GitHub, GitLab, Bitbucket and Azure DevOps, with CLI and editor options too. Copilot reviews GitHub pull requests and offers Azure DevOps review in public preview, alongside its supported development interfaces. GitHub Copilot code review reaches several interfaces, but Git-host coverage should be described carefully. GitHub.com is the native pull-request surface, and Azure DevOps support is currently public preview. Editor and CLI reviews can inspect local work without turning another Git provider into a GitHub pull request. Platform fit can settle the shortlist before review quality is measured. A Bitbucket or GitLab team gets a documented CodeRabbit pull-request integration. A GitHub organisation may value Copilot's rulesets, automatic review policy, licence administration, and direct path to Copilot cloud agent. GitHub says automatic reviews can run when a pull request opens, leaves draft state, or receives another push, depending on settings. Reviewers can also request another Copilot pass manually. CodeRabbit supports automatic and explicit review, including the concrete `@coderabbitai review` trigger used in our review workflows. Do not enable every trigger during the first week. One review after a meaningful implementation change gives cleaner evidence than a comment stream after every intermediate commit. Add automatic review of new pushes only after the team knows how repeat findings and usage are handled. ## How Much Do CodeRabbit and Copilot Reviews Cost? CodeRabbit charges per developer with plan-specific review allowances; Copilot review consumes AI credits under its paid plans and may also incur Actions runner charges. Compare total monthly usage rather than treating either subscription as unlimited review. Copilot code review is available on paid Copilot plans, yet each review consumes AI credits. Agentic context gathering and tool use can also consume GitHub Actions minutes. GitHub says the amount depends on the model and tokens processed. For organisations, [GitHub lists Business at $19 per user per month](https://docs.github.com/en/copilot/concepts/billing/organizations-and-enterprises) with 1,900 AI credits and Enterprise at $39 with 3,900 credits. Extra use costs $0.01 per credit, subject to budgets and current terms. | Dimension | CodeRabbit | GitHub Copilot | | Review price | Separate subscription | Paid plan feature | | Usage unit | Reviews and files | AI credits and minutes | | Budget isolation | Review-specific limits | Shared Copilot pool | | Extra use | Usage add-on available | $0.01 per credit | CodeRabbit charges separately for review, with published usage limits. Copilot can have a lower adoption barrier for a licensed GitHub team, while review shares credits and infrastructure with other Copilot features. Compare the total cost with the number of real bugs each reviewer catches. Include subscription or credit spend, runner minutes, and the human time used to prove or dismiss each comment. An included feature can still cost you time and use credits needed for other work. Check the billing owner for manual, automatic and bot-authored reviews before enabling them across your organisation. The [Copilot pricing guide](/ai-code-review/github-copilot/#copilot-review-pricing) covers attribution and budgets. Keep CodeQL or GitHub Code Quality findings separate when measuring what Copilot itself caught. ## Test the Current Products Use a repository with project instructions and a working test suite. Select pull requests that expose context, security, generated-code, and ordinary maintenance paths. Keep human review unchanged so misses remain observable. 1. Give both tools the same three repository rules. 2. Run one manual review after each change is ready. 3. Confirm whether full context loaded for both products. 4. Label each comment by outcome and duplicate status. 5. Record AI credits, runner minutes, and review time. 6. Repeat one case with context gathering unavailable. Also test a pull request that changes its own review instructions. Confirm which rules each tool reads and whether the reviewer calls attention to that change. You need to know which instructions the reviewer followed, especially when the pull request can change them. Include ordinary changes as well as known bugs. Record Copilot's review effort, which defaults to Balanced unless a higher-priority setting overrides it, and CodeRabbit's profile and enabled tools. Give each product one configuration pass rather than tuning indefinitely until your preferred tool wins. ## What Has Our Experience With CodeRabbit and Copilot Shown? We have used CodeRabbit in our release workflows. Forced Copilot review was removed from the main repository rules on July 19, 2026 after its comments proved noisy in that workflow. GitHub's product has changed since that decision, adding full-project context, instructions, skills, AI credits, and review effort. ## Read the Full Tool Guides For setup and troubleshooting: - [CodeRabbit guide](/ai-code-review/coderabbit/) covers configuration precedence, local review, finding triage, pricing limits, and a measured pilot. - [GitHub Copilot review guide](/ai-code-review/github-copilot/) covers effort levels, context setup, AI credits, runner failures, and our experience changing review rules. - [AI code review comparisons](/ai-code-review/comparisons/) links the other matchups when a dedicated reviewer or GitHub-native review is only part of the shortlist. ## Frequently Asked Questions ### Is Copilot code review included in Copilot plans? GitHub says Copilot code review is available on paid Copilot plans. Each review consumes AI credits, and agentic context gathering can also use GitHub Actions runner minutes. Organisation policies, budgets, and paid extra usage determine what happens after included credits are exhausted. ### Can Copilot review use full project context? Yes, GitHub Copilot code review can gather full-project context for more specific feedback. That agentic work depends on repository setup and runner access, so a team should test both the normal path and the reduced-context path when setup fails. ### Can CodeRabbit and Copilot use repository instructions? Yes, CodeRabbit and GitHub Copilot can use repository-level instructions during a review. CodeRabbit uses its configuration, knowledge base, guideline files, and path instructions. GitHub Copilot code review can read repository custom instructions, AGENTS.md, path-specific instructions, and relevant skills from the pull request's head branch, subject to current policy. ### Does Copilot review replace CodeQL or human review? No, Copilot code review does not replace CodeQL or human review. Its feedback can be wrong and needs checking. GitHub Code Quality and CodeQL provide separate rules-based and hybrid checks, while the team remains responsible for the final merge decision. ## My Verdict CodeRabbit wins the head-to-head when review needs its own cross-platform product, tool controls, configuration owner, and predictable review-specific limits. GitHub Copilot code review makes more sense when the organisation wants repository-aware review inside its existing GitHub policy and Copilot system, and it accepts the shared AI-credit and runner model. CodeRabbit gives review its own controls and budget. Copilot consolidates review inside the GitHub and Copilot platform. The old diff-only argument no longer holds, so compare what each tool reads, the bugs it finds, its cost and what happens when a review fails. --- ## CodeRabbit vs Greptile: Two Paid AI Reviewers Compared URL: /ai-code-review/comparisons/coderabbit-vs-greptile/ Cluster: AI Code Review > Comparisons Choose CodeRabbit for configurable reviews with integrated analysis tools across GitHub, GitLab, Bitbucket or Azure DevOps. Consider Greptile when your main problem is tracing bugs through a large GitHub or GitLab codebase. Both can use context beyond the changed lines; calling CodeRabbit a diff-only reviewer would be misleading. Greptile builds a graph of functions, classes and dependencies. CodeRabbit combines repository context, linked repositories, instructions and static checks. The useful question is whether either approach catches the bugs your team misses, at a cost you can justify. We have used CodeRabbit on our projects. My Greptile assessment is based on its documentation. I have not tested both tools side by side. For a wider shortlist, see the [guide to AI code review tools](/ai-code-review/). The [CodeRabbit guide](/ai-code-review/coderabbit/) and [Greptile guide](/ai-code-review/greptile/) explain setup, rules and how to check findings. ## The Matchup at a Glance Both products support repository guidance and feedback-based learning. Both can suggest fixes and offer enterprise deployment controls. They differ in supported Git hosts, built-in static checks, how they read related code and how they charge for reviews. | Dimension | CodeRabbit | Greptile | | Context model | Knowledge and linked repos | Repository graph | | Static tools | 40-plus integrations | Agent-centred review | | Learning | Learnings and replies | Reactions and replies | | Git hosts | Four documented hosts | GitHub and GitLab; more Enterprise hosts | | Team meter | Developer plan limits | Seats plus credits | | Agent handoff | Autofix and actions | Fix with your Agent | A codebase graph is useful only if it helps find bugs. Check whether Greptile identifies the affected code and explains the failure, including when part of its index is unavailable. ## How Do CodeRabbit and Greptile Understand Your Codebase? CodeRabbit gathers repository and linked-repository context for its reviews; Greptile builds a graph of selected codebases before reviewing changes. Its overview names functions, classes, and dependencies as graph elements. Pull-request reviews use that graph to reason about changes beyond the shown lines. Greptile also says reviews typically post in about three minutes. Treat that timing, from its [official product overview](https://www.greptile.com/docs/introduction), as a vendor description that may not hold for every repository. CodeRabbit draws context from changed code, repository guidance, a knowledge base, feedback learnings, and linked repositories. Multi-Repo Analysis lets a review inspect related repositories for breaking changes. The number of linked repositories depends on the plan. The [CodeRabbit Multi-Repo Analysis guide](https://docs.coderabbit.ai/knowledge-base/multi-repo-analysis) says linked repositories must use the same Git platform. Each repository must also be accessible to the bot. Inaccessible context can be skipped with a warning. A good context test changes a shared response type and leaves one consumer outside the diff. A stronger test puts that consumer in a linked repository. Ask both products to identify the consumer, explain the break, and cite the relevant files. Then remove or deny part of the context. The reviewer should make the limitation visible. A clean report produced after a failed index or inaccessible linked repository carries less assurance than a clean report with the expected context loaded. ## What Do CodeRabbit and Greptile Include in a Review? CodeRabbit combines AI findings with configured linter and security-tool output; Greptile presents agent findings, explanations and suggested fixes, and can learn from team feedback. CodeRabbit's official tool documentation lists more than 40 integrations and provides `Chill` and `Assertive` profiles. Teams can tune individual tools through `.coderabbit.yaml` or settings. That design gives a reviewer two kinds of evidence. A static tool may cite a rule, file, and line. An AI comment may explain a behavioural risk that no fixed rule captures. The team still has to distinguish reproducible findings from plausible suggestions. Greptile's output centres on agent findings with descriptions and suggested fixes. Its documentation says team reactions and replies teach the system what matters, with reduced unwanted feedback after two to three weeks. The published learning period changes the design of a useful pilot. A one-day comparison may capture installation and first-review quality, while missing the behaviour Greptile says appears after feedback accumulates. Run enough ordinary pull requests to test whether repeated low-value comments decline. Apply the same outcome labels to both products: confirmed defect, useful inspection, protected behaviour, duplicate, and noise. Record the evidence type beside every finding as well. A scanner can point to a broken rule. An AI finding needs to explain how the code could fail. Check the evidence for both. ## How Do You Set Rules for CodeRabbit and Greptile? CodeRabbit accepts review settings and path instructions in .coderabbit.yaml; Greptile accepts custom rules and supporting context, with availability depending on the plan. A repository can set profiles, filters, path instructions, enabled tools, and review behaviour. Organisation controls can centralise or override policy across repositories. Greptile accepts custom rules, style guides, documentation, and free-form context. Rules can be scoped to repositories and file patterns. Its documentation also says it can infer learnings from developer comments and reactions. Greptile's [official custom context guide](https://www.greptile.com/docs/code-review-bot/custom-context) documents these controls. The current pricing page puts custom rules on Pro, so a free-plan pilot should verify which controls are available before comparing configured behaviour. ``` {`# Shared rule for the comparison pilot Billing writes must: - verify the authenticated tenant, - preserve idempotency, - emit an auditable event, - include a failure-path test.`} ``` Use one specific rule like this in both products. Avoid copying an entire engineering handbook into each prompt. One specific rule makes it easier to see whether the reviewer found and understood the problem. CodeRabbit's extra question is whether an existing tool already enforces part of the rule. Enable the tool when deterministic evidence is available. Greptile's extra question is whether the repository graph connects the changed operation to the event consumer or idempotency store. ## Can CodeRabbit and Greptile Help Fix Reported Bugs? CodeRabbit can propose and apply fixes through its supported actions, while Greptile sends findings to a coding agent through Fix with your Agent or Fix All. It can send issue context to Claude Code, Codex, Conductor, Cursor, or Devin. The vendor says the handoff includes file paths, line numbers, and suggested code. Those handoffs shorten the path from a confirmed comment to a proposed patch. They do not prove the patch preserves surrounding behaviour. CodeRabbit offers suggested fixes, autofix, and broader workflow actions depending on plan and platform. Its Team plan and the plans above it add work around the review, including unit-test generation and merge-conflict resolution. The [official Finishing Touches guide](https://docs.coderabbit.ai/finishing-touches) documents current commands and platform differences. Confirm the defect before requesting a fix, then inspect and test the patch. A Fix All action can mix a real bug with speculative clean-ups; separate those changes so you can tell what each repair does. ## How Much Do CodeRabbit and Greptile Cost? [CodeRabbit Essentials starts at $24 per developer each month on annual billing](https://docs.coderabbit.ai/management/plans) or $30 month to month, according to its plans page. Team costs $48 per developer per month billed annually or $60 month to month. Advanced costs $72 per developer per month billed annually or $90 month to month. Free and open-source access have their own review limits and entitlements. On its own pricing page, [Greptile Starter is free](https://www.greptile.com/pricing) for one active developer with 50 credits each month. Pro costs $30 per seat each month, includes 50 credits per seat, and charges $1 for each additional credit. Greptile prices one standard review at one credit. | Dimension | CodeRabbit | Greptile | | Free entry | Free and OSS plans | One-developer Starter | | Paid floor | $24/dev/mo (annual) | $30/seat/mo | | Usage meter | Hourly plan limits | Credits per review | | Overage | Usage add-on | $1 per credit | The usage limits matter most when your team opens many pull requests. CodeRabbit publishes per-developer hourly review and file limits, with a usage add-on on paid plans. Greptile includes a monthly credit pool per seat and then meters additional credits. Estimate your author count, seats, standard reviews, higher-cost Plus or Apex reviews and extra charges. Include the time people spend checking comments. A cheaper base plan can cost more when its output creates duplicates or when review volume crosses the included allowance. The tool pages carry full tier descriptions. CodeRabbit starts lower when billed annually, but Greptile Starter may be enough for a solo developer. Compare the features and review allowance you need before choosing a paid plan. ## Where Can You Run CodeRabbit and Greptile? CodeRabbit documents GitHub, GitLab, Bitbucket, and Azure DevOps. Greptile documents GitHub Cloud, GitHub Enterprise Server, GitLab Cloud, and GitLab Self-Managed. CodeRabbit supports Bitbucket and Azure DevOps. Greptile Enterprise now lists Bitbucket Data Center, Gitea and on-premises Perforce with P4 Code Review, so distinguish those from ordinary cloud-plan support. A GitHub or GitLab team can compare both on product behaviour rather than eliminating one by host. Greptile offers cloud and self-hosted deployment, including Docker Compose, Kubernetes, and air-gapped environments. Its Enterprise pricing includes an option to self-host in the buyer's infrastructure. CodeRabbit Enterprise also lists self-hosting, role controls, audit logging, and marketplace billing. Do a separate security review before connecting sensitive source. Ask about data flow, retention, model providers, access revocation, audit evidence, regional requirements, and incident handling. Self-hosting does not settle questions about data retention or model-provider access. ## Test the Context Claim A useful pilot begins with known contract breaks rather than random pull requests alone. Seed a change that breaks a distant caller, a shared schema, or a cross-repository dependency. Keep the expected failure and protecting test hidden from the reviewer until after results are recorded. 1. Connect one representative repository to both products. 2. Confirm indexing and linked context completed successfully. 3. Apply the same local rule to the affected path. 4. Run one review after the pull request is ready. 5. Record cited files, confirmed findings, and misses. 6. Repeat with one context source unavailable. 7. Continue on ordinary work for the learning period. Include small self-contained changes in the ordinary sample. They reveal whether graph or knowledge setup adds useful information when the risk lives entirely in the diff. The tool still needs to be worth its cost on routine changes. Review the result with the people who author and approve pull requests. Authors know which comments interrupt work and which help them fix bugs. Maintainers can spot recurring problems and missed issues. A tool that helps only one group may struggle to remain part of the process. ## Follow the Full Tool Guides For setup and configuration details: - [CodeRabbit guide](/ai-code-review/coderabbit/) covers configuration precedence, tool output, local review, plan limits, and a controlled pilot. - [Greptile guide](/ai-code-review/greptile/) covers graph testing, feedback learning, custom context, agent handoffs, credits, and deployment questions. - [AI code review comparisons](/ai-code-review/comparisons/) links alternatives when neither context design fits the host, budget, or workflow. ## Which AI Reviewer Would I Choose? I would start with CodeRabbit when a team wants configurable reviews across more Git hosts, values static-tool evidence alongside AI comments, and wants a seat-based starting price with optional overages. Greptile earns the decision when repository-graph context and direct coding-agent handoffs solve a demonstrated cross-file problem on GitHub or GitLab. Neither design guarantees better reviews. Test a known contract break, an incomplete-context state, and several ordinary pull requests before deciding which design earns a permanent place. ## Frequently Asked Questions ### Does Greptile index the whole repository? Yes, Greptile indexes selected repositories as a graph of functions, classes and dependencies, then uses that context during pull-request review. A team should still test whether the reviewer finds a known consumer outside the diff and confirm current retention and deployment terms. ### Does CodeRabbit have cross-repository context? Yes, CodeRabbit supports cross-repository context through Multi-Repo Analysis, with linked-repository limits that vary by plan. Linked repositories must be on the same Git platform and accessible to the bot, which narrows the old context gap although its design differs from Greptile's repository graph. ### Is CodeRabbit or Greptile cheaper for one developer? CodeRabbit's published Essentials price starts below Greptile Pro for a private repository, while both vendors also publish free access with different conditions. Greptile has a free one-developer Starter plan, and CodeRabbit has Free and open-source plans, so required features decide the useful comparison. ### Can Greptile send a finding to Codex? Yes, Greptile can send a finding to Codex through Fix with your Agent. Its documented handoffs also include Claude Code, Cursor, Conductor and Devin. The handoff includes issue context, file paths, and suggested code, which still need a reviewed diff and normal tests before the generated repair is treated as resolved. Product details were checked against the vendors’ official documentation on July 28, 2026; pricing was checked on August 8. CodeRabbit’s revised plans and prices were checked on October 1, 2026. --- ## CodeRabbit vs Qodo: Which AI Reviewer Should You Use? URL: /ai-code-review/comparisons/coderabbit-vs-qodo/ Cluster: AI Code Review > Comparisons I would start with CodeRabbit for a configurable, dedicated reviewer with a per-developer subscription. Consider Qodo if central review standards, cross-repository checks or Enterprise deployment options are the deciding factors. Both support the four main Git providers, and both have broader context than a simple diff reader. We have used CodeRabbit in our release workflows. I have assessed Qodo from its documentation, rather than a hands-on trial. The [AI code review hub](/ai-code-review/) covers the wider choice of tools. ## At a Glance | Comparison criterion | CodeRabbit | Qodo | | Automatic trigger | Pull request and updates | Publication or every push | | Rules file | `.coderabbit.yaml` | `.pr_agent.toml` | | Central rules | Config hierarchy and overrides | Portal and Rule System | | Cross-repo tier | Plan-linked repositories | Enterprise capability | | Git providers | Four documented providers | Four documented providers | | Provider caveat | Features vary by platform | Plans and setup vary | | Entry price | $24/dev/mo annual, $30/dev/mo monthly | $30/mo credit pack | | Higher tier | Team $48/dev/month; Advanced $72/dev/month, billed annually | Custom Enterprise terms | | Free access | Summary, CLI, and IDE | Trial; qualified OSS programme | CodeRabbit charges by seat, while Qodo draws from a shared credit pool. Qodo also documents four-provider support, although its troubleshooting guide attaches different plan and setup conditions to those providers. Check that your plan supports your Git host before trying the reviews. ## How Do CodeRabbit and Qodo Review Pull Requests? CodeRabbit and Qodo can automatically review pull requests and new commits, then post findings for the team to check. CodeRabbit also accepts `@coderabbitai review`; current Qodo provides review discussions and fix requests rather than simply reproducing the old PR-Agent command workflow. Current Qodo Code Review also starts automatically. Qodo says a review can run when a pull request is published or on every push, then posts severity-ranked findings that a reviewer can discuss, dismiss, or ask Qodo to fix. This is different from the command-driven PR-Agent workflow still described in legacy documentation. Both products expose a review-specific configuration file. CodeRabbit's YAML controls profiles, tools, summaries, path instructions, and review behaviour. Qodo's TOML controls automation, feedback presentation, provider-specific commands, and finding thresholds. Use those files to give both reviewers the same rules during your trial. ## How Do CodeRabbit and Qodo Manage Review Rules? CodeRabbit manages review rules through configuration files and central overrides; Qodo combines review configuration with its portal-based Rules System. Workspace and organisation global overrides sit above a repository's `.coderabbit.yaml`, followed by a central `coderabbit` repository and lower UI defaults. The pull-request comment can show which source supplied the active configuration. That hierarchy gives an administrator a way to enforce one rule across repositories. It also means a maintainer can edit local YAML and see no change because an organisation-wide setting overrides it. The [central configuration guide](https://docs.coderabbit.ai/configuration/central-configuration) explains what to check when a local setting has no effect. Qodo's current configuration can come from a repository file, a project or group settings repository, an organisation file, or the portal. Its Qodo 2 Rule System also turns standards into scoped rules and reports duplicate, conflicting, or dated rules. Qodo's February 2026 release material labels that system as beta and initially limits it to GitHub deployments. Both tools need someone to maintain their rules. CodeRabbit needs someone who can trace precedence and keep profiles, paths, and tools small. Qodo needs someone who can separate review settings from governed rules, check provider eligibility, and decide whether learned or suggested rules deserve enforcement. Use one deliberately conflicting test before wider rollout. Put a broad organisation rule above a narrow repository rule, then inspect which instruction the reviewer follows. A clear comment produced from the wrong source is still a configuration failure. ## What Review Reports Do CodeRabbit and Qodo Provide? Qodo reports rule sources, scope and violations; CodeRabbit provides review metrics and exports that help teams inspect review activity. Its 2026 release material says the portal tracks each rule's source and scope, spots duplicates and conflicts, flags dated rules, and reports adoption and violations. Administrators can use those records to see which rules are being applied. Check which Rule System features your plan and Git host support. Qodo labels parts of the Rule System as beta, and the February release initially names GitHub single-tenant and multi-tenant deployments. A GitLab, Bitbucket, or Azure DevOps buyer should confirm which rule features have reached its exact deployment before scoring them. CodeRabbit takes a different path for reporting review activity. Its January 2026 changelog added pull-request data export and a Review Metrics API with date, repository, and user filters. Exported fields include review time plus comment breakdowns by severity and category. Neither dataset proves a rule is good. A high violation count can mean the rule catches a recurring defect, or that it describes a preference developers reject. Pair rule metrics with confirmed fixes, dismissed comments, and the human time used to resolve them. Ask who can edit rules, who can enforce an override, and who reviews rule health each month. Qodo gives the governance owner a centralised rule model. CodeRabbit gives the review owner configuration sources and exportable review records. Both require someone to check that the rules are still useful. ## What Can CodeRabbit and Qodo Check? CodeRabbit checks code with AI plus configurable linters and security tools; Qodo uses multi-agent analysis, repository context and review rules to flag bugs and standards violations. CodeRabbit's profile changes how much feedback appears, while path instructions and tool settings narrow the work for particular directories. Those settings let you tune the comments, but you still need to try the default review on your code. Qodo describes multi-agent analysis with full-repository context, pull-request history, a Rule System, severity-ranked findings, cross-repository review, and controls for verbosity. Those are vendor claims about the current service. A buyer can still test the meaningful difference. Give each tool the same repository rule, open a change that violates it outside the edited line, and record whether the finding cites the affected path. Use the same labels for confirmed defects, duplicates and false alarms when [comparing AI code reviewers](/ai-code-review/comparisons/). ## Can CodeRabbit and Qodo Review Across Repositories? Yes, CodeRabbit and Qodo can review across repositories, but access depends on the plan and connected codebases. CodeRabbit's current plan page allows one linked repository on Essentials, five on Team, ten on Advanced, and twenty on Enterprise. Qodo's current pricing page places cross-repository capabilities in Enterprise. Those limits matter when testing a change that affects another repository. A shared schema change can look safe inside one service and break a consumer in another repository. Link only the repositories needed for that contract, then ask each product to name the consumer and the incompatible field or call. Run the same pull request with the linked context removed. The second result shows whether the reviewer marks reduced coverage or posts a clean-looking review without the related code. A review cannot rule out a bug in code it could not read. Do not score a vendor claim such as “full repository context” as a finding. Record the exact file or repository used in the explanation, plus the path that makes the defect reachable. The [Qodo guide](/ai-code-review/qodo/) and [CodeRabbit guide](/ai-code-review/coderabbit/) explain how to configure each reviewer. ## Can CodeRabbit and Qodo Review Code Before a Pull Request? Yes, CodeRabbit and Qodo offer local review before a pull request is opened. CodeRabbit documents editor extensions and a CLI for uncommitted work. Qodo is moving local review into coding agents through its Agentic Toolbox. Its [official IDE listing](https://marketplace.visualstudio.com/items?itemName=Codium.codium) says the IDE plugin retires on 30 October 2026; it has not yet retired at the time of this revision. Reviewing locally can catch bugs before colleagues see the pull request. It does not replace the team's pull-request checks. A local result may use different settings, credentials, code state, or context from the hosted review. Use local findings to prepare your changes; colleagues still need to review the pull request. ## Pricing CodeRabbit Essentials costs $24 per developer each month with annual billing or $30 month to month, according to its plans page. CodeRabbit Team costs $48 per developer per month billed annually or $60 month to month; Advanced costs $72 or $90 per developer per month on the same respective billing terms. The Free plan allows unlimited public and private repositories, but private hosted pull-request review becomes summarisation-only after the 14-day Advanced trial; code review remains available through the IDE and CLI. Qualifying public open-source projects receive Team features through a separate OSS plan. Qodo Pro Team starts at $30 for 2,500 pooled credits, which its pricing page estimates at about 18 reviews. Credits cost $0.012 each, larger or more complex pull requests consume more, and unused credits expire at the end of the monthly cycle. Qodo offers a 14-day no-card trial rather than a permanent general free tier. Qualified open-source projects can apply for free access through a separate programme. Published plan terms. | Dimension | CodeRabbit | Qodo | | Billing unit | Paid developer seats | Pooled monthly credits | | Paid floor | $24/dev/mo annual, $30/dev/mo monthly | $30/mo for 2,500 credits | | Usage meter | Five reviews hourly, Essentials | $0.012 per credit | | Free entry | Free plan, summaries only | 14-day trial only | The cheaper option depends on your author count and review volume. A steady group of pull-request authors can model CodeRabbit by paid seats and review limits. For Qodo, estimate credit use and set a limit on extra spending. Neither published price predicts cost without the actual author count or pull-request sample. Qodo's pack estimate is a planning aid, not a fixed review price. Its pricing page says larger or more complicated pull requests consume more credits, unused credits expire each month, and the buyer can set an overage cap. Record credits per pull request rather than assuming every review costs the vendor's average. CodeRabbit's hourly limits matter most on busy release days. Essentials publishes five pull-request reviews per developer per rolling hour, Team publishes eight, and Advanced publishes ten, with a paid usage add-on for work beyond the limit. A release day can hit that ceiling even when the monthly author count looks cheap. Forecast both from the same four weeks of pull requests. For CodeRabbit, record paid authors, peak reviews per hour, and files per review. For Qodo, record credits, pull-request size, reruns, and expired balance. For both tools, include the time spent checking comments. ## Where Can You Run CodeRabbit and Qodo? CodeRabbit and Qodo both support GitHub, GitLab, Bitbucket and Azure DevOps, with Enterprise options for self-hosted deployment. The label "supports four providers" still hides commercial and operational differences. Qodo's current troubleshooting documentation says GitHub, Bitbucket, and GitLab Cloud are supported on Teams, while Enterprise customers must confirm their registered organisation and Azure DevOps path. Its setup steps also differ by provider. CodeRabbit currently lists self-hosting among its Enterprise options. Qodo lists single-tenant SaaS, on-premises, and air-gapped deployment on Enterprise. A data-residency decision therefore needs the contract and architecture for the purchased service. Community source code cannot define those commercial terms. For sensitive code, confirm where agents run, where code and findings travel, and which people can change review rules. A self-hosting option does not settle model-provider access or data retention; check the purchased service's architecture and terms. ### What Changes Between Git Providers? Qodo's current troubleshooting guide says GitHub, Bitbucket, and GitLab Cloud authors need an eligible Teams seat and a linked Git account. Enterprise buyers must also confirm the registered Git organisation, and Azure DevOps follows an Enterprise setup path. A missing link or registration can cause the service to skip review. CodeRabbit's central configuration has its own platform limits. Azure DevOps needs a separate `coderabbit` configuration repository in each project, while Bitbucket Server does not yet receive central configuration. A team can have pull-request review on a provider without receiving the same organisation-wide configuration path. ## How Can You Tell Whether CodeRabbit or Qodo Skipped a Review? Check the review status and quota messages for CodeRabbit or Qodo; an empty comment thread does not prove a review ran. Qodo's troubleshooting guide says reviews can be skipped when the author lacks a paid seat, the Git account is not linked, the organisation registration is missing, or the monthly review limit is reached. Provider webhooks and secrets can fail separately. CodeRabbit can hit a rolling hourly limit or a file limit. Its walkthrough can show remaining review quota and refill timing, and paid plans can use a usage add-on. During a trial, capture the status that appears when a limit blocks or narrows a review. Add one controlled failure for each product. Remove a required provider permission, exhaust a small test budget where safe, or disable expected cross-repository access. The author should be able to tell “review skipped” from “review ran and found nothing” without opening an administrator dashboard. This test often changes the purchasing decision for a larger team. Even an accurate reviewer cannot help if it skips the pull request. Make sure someone sees skipped reviews and knows how to restart them. ## Is Qodo the Same Product as PR-Agent? No, current Qodo Code Review and community PR-Agent are separate products. Qodo announced in April 2026 that PR-Agent was moving to the independent The-PR-Agent organisation under Apache 2.0 governance; the repository's license is MIT today. Qodo's own announcement calls it a community-led project that shares an origin with Qodo but has not kept pace with the commercial platform. PR-Agent remains useful for teams that specifically want a self-hosted open-source reviewer and accept the operating work. It is no longer accurate to treat its slash commands, provider issues, or deployment model as the implementation details of current Qodo Code Review. Compare CodeRabbit with current Qodo for a hosted purchase. Compare CodeRabbit with community PR-Agent for an open-source deployment decision. ## Run the Same Pull Requests Twice Start with ten to twenty recent pull requests whose outcomes are already known. Include an authorisation boundary, a schema or API contract, generated output, a cross-repository consumer, and routine maintenance. Keep the human review record so missed defects remain visible. 1. Install each product on one repository without broad automatic triggers. 2. Give both products the same three testable repository rules. 3. Run each review once on the same ready commit. 4. Record confirmed defects, wrong findings, repeats, misses, and time. 5. Record the active configuration source and context that loaded. 6. Make one documented tuning pass, then run the sample again. Compare findings, misses, review time and cost after one configuration adjustment. Running community PR-Agent means maintaining its models, updates and webhooks. That is a different buying decision, covered in the [Qodo guide](/ai-code-review/qodo/). ## The CodeRabbit and Qodo Verdict Choose CodeRabbit when a per-developer plan and a review workflow we have used on our GitHub projects fit the team. Shortlist Qodo when pooled credits, current four-provider support, or an Enterprise deployment option solves a real constraint, then run a provider-specific pilot. Choose community PR-Agent only as a separate open-source deployment decision. ## Frequently Asked Questions ### Is current Qodo Code Review still PR-Agent? No, Qodo describes PR-Agent as a community-maintained legacy project that is distinct from its current commercial review platform. The shared history does not make old PR-Agent commands, deployment options, or issue reports evidence of current Qodo Code Review behaviour. ### Does CodeRabbit include free pull-request review? CodeRabbit's Free plan covers unlimited public and private repositories, but hosted pull-request review on private repositories becomes summarisation-only after the 14-day Advanced trial. Qualifying public open-source projects receive Team features without a paid subscription. ### Can a team self-host CodeRabbit or Qodo? Yes, CodeRabbit and Qodo offer self-hosted deployment through Enterprise arrangements. Qodo also lists single-tenant SaaS and air-gapped options. Community PR-Agent remains self-hostable, but it is now a separate legacy project and should be evaluated as a different product. ## Read the Full Guides - [CodeRabbit guide](/ai-code-review/coderabbit/) for configuration and plan details. - [Qodo guide](/ai-code-review/qodo/) for current review and credit controls. - [AI code review comparisons hub](/ai-code-review/comparisons/) for the shared triage method. --- ## Codex vs Claude Code: AI Code Review Compared URL: /ai-code-review/comparisons/codex-vs-claude-code/ Cluster: AI Code Review > Comparisons Codex is my starting point for routine GitHub reviews when its serious-issue focus fits the job. Managed Claude Code Review is worth comparing when your team wants adjustable review depth and verification by several agents, with separate usage billing. Both can find bugs; neither turns a clean report into proof that a pull request is safe. I run Codex reviews in our release workflows and use Claude Code as the daily coding environment. That is not a claim that I have benchmarked Anthropic's managed reviewer. For the wider shortlist, see my [AI code review tools guide](/ai-code-review/). ## At a Glance | Comparison criterion | Codex | Claude Code Review | | Review threshold | P0/P1 default on GitHub; P2 on request | Important, Nit, Pre-existing | | Pipeline | Repository-aware GitHub review | Multi-agent with verification | | Rules | `AGENTS.md` | `REVIEW.md` and `CLAUDE.md` | | Manual trigger | `@codex review` | `@claude review` | | Availability | Qualifying ChatGPT plans | Team and Enterprise | | Review charge | Plan allowance and credits | Separate token-based usage credits | | Review time | No duration commitment | Varies with review level and change | | Failed run | No published retry contract | Neutral; retry depends on failure | The practical choice is between a focused review drawing on your Codex allowance and a separately billed Claude service with Quick, Standard and Deep levels. Standard is Claude’s default. A higher setting buys more analysis, not a guarantee of more useful findings. ## Which Codex and Claude Review Options Are Being Compared? Codex has a managed GitHub reviewer, local review in Codex clients, and a `codex-action` building block for GitHub Actions. Claude Code has a managed GitHub App, the `/code-review` command, and Claude Code Action. Each option has its own setup and costs. With managed review, the vendor runs the review service. OpenAI controls the Codex GitHub review threshold and posts the review through its connected cloud service. Anthropic runs its fleet and verification work on Anthropic infrastructure, then posts a neutral check with findings. Local review gives one developer a private pass before the pull request. With an action, your team configures the runner, trigger, instructions, provider, permissions and how findings are posted. Comparing a local review with a managed review also means comparing different setup work, permissions and costs. The managed GitHub services review pull requests on your repository. Local reviews and team-owned actions require a different setup. Use the [Codex guide](/ai-code-review/codex/) and [Claude Code review guide](/ai-code-review/claude-code/) when the decision is local review or a team-owned action. Check which service your subscription pays for. A Claude Code subscription used for daily local work does not include managed review spend, and an OpenAI API key does not unlock native Codex GitHub review. Team-owned actions create their own provider and runner bills. ## What Do Codex and Claude Code Review Check? Codex reviews changed code with repository guidance and defaults to P0/P1 findings on GitHub, with P2 available on manual request. Managed Claude Code Review investigates bugs through multiple agents and verification, with Quick, Standard and Deep levels. These settings describe intended coverage, not guaranteed accuracy. Anthropic says managed Code Review analyses the diff with surrounding full-codebase context. Specialised agents look for logic errors, security vulnerabilities, broken edge cases, and regressions, then a verification step checks candidate findings before posting them. Findings are classified as Important, Nit, or Pre-existing. The products use different rules for deciding which findings to post. Codex filters the posted review to its two highest priorities. Claude uses a wider severity vocabulary and lets teams tune scope and reporting through `REVIEW.md`, within its protected review constraints. Try both on the same code before deciding which finds more useful bugs. ## How Do You Configure Codex and Claude Code Review? Configure Codex review guidance in applicable `AGENTS.md` files. Managed Claude Code Review uses `REVIEW.md` and `CLAUDE.md`; its review instructions come from the base or default branch, not the proposed head branch. ``` {`## Code Review Rules - Report tenant writes without organisation scoping. - Cite the unprotected execution path. - Ignore formatting already enforced by CI.`} ``` Managed Claude Code Review uses `CLAUDE.md` for project context and `REVIEW.md` for review guidance. It reads review instructions from the base or default branch, never from the pull request’s own changes. Directory-level guidance can narrow the rules for particular code. Keep essential rules in the file itself because `@` imports are not expanded. The local `/code-review` command is a separate workflow. Anthropic says local review follows `CLAUDE.md` but does not read `REVIEW.md`. Teams should avoid assuming a rule tested locally will govern the managed GitHub service in the same way. ## Pricing Codex GitHub review is available through qualifying ChatGPT plans. Plus starts at $20 per month and includes cloud integrations such as automatic code review. OpenAI also lets eligible users buy credits to extend usage, so "included" describes an allowance rather than unlimited zero-cost review. API-key access does not include GitHub cloud review. It supports Codex through the CLI, SDK, IDE extension, or custom automation, billed under the API rather than the native GitHub integration. OpenAI's live rate card now separates most workspaces on token-based credit rates from a small Enterprise group still on its legacy card. The older estimate of roughly 34 credits per pull request appears only in that legacy section. Check the workspace's Codex Usage panel before forecasting review volume. Anthropic bills managed Code Review to the organisation through token-based usage credits, separately from the Team or Enterprise plan’s included usage. There is no flat per-review price. Review level, change size, surrounding code and verification work affect the bill. The [current billing documentation](https://code.claude.com/docs/en/code-review-costs) explains the spend limits. Published plan terms. | Dimension | Codex review | Claude Code Review | | Access path | Qualifying ChatGPT plans | Team and Enterprise | | Published price | Plus from $20/mo | Variable token-based cost | | Billing unit | Plan allowance, then credits | Usage credits per review | | Off the integration | CLI, SDK, IDE extension | Local review command | A trigger changes the Claude bill directly. Once-after-creation runs once, every-push can multiply reviews, and manual mode waits for `@claude review`. Anthropic provides a spend cap and per-repository cost reporting. Codex also has finite plan usage, so both trials need an actual cost record. ## Can Codex or Claude Code Review Control Merges? Neither managed Codex review nor managed Claude Code Review provides a documented replacement for required human approval. Claude findings do not produce a failing check; Codex’s review comments likewise should not be treated as an autonomous approval. Keep branch protection and tests as the merge controls. Start either trial with one review after the pull request is ready. Keep tests and human approval unchanged, then record accepted defects, dismissals, duplicate comments, elapsed time, and credits or usage spend. The shared triage definitions live on the [AI code review comparisons hub](/ai-code-review/comparisons/). Keep a second reviewer only if the extra bugs it catches justify its cost and the wait. The Claude command you choose affects how often you pay for a review. `@claude review` and `@claude review once` both start one run without subscribing, while `@claude review always` subscribes the pull request to reviews on later pushes. Repeat billing is opt-in, so the repository trigger setting decides most of the cost. Codex automatic review runs when a new pull request is opened for review. Manual `@codex review` is easier to line up with the exact commit a team wants inspected. Record the head commit beside each result so a later patch cannot make an old review look current. ## What Happens When a Codex or Claude Review Fails? Claude Code Review retries an interrupted session once automatically, but other failures need a new request; OpenAI does not publish an equivalent retry contract for managed Codex review. Check that either review completed before relying on an empty findings list. For other failed or timed-out Claude runs, use `@claude review` or the GitHub Re-run button where supported; Re-run is unavailable for forks. Neither manual recovery subscribes the pull request to later pushes. Reaching the monthly spend cap creates a separate skipped-review comment. Check the reason before treating a missing review as a completed one. OpenAI documents the normal Codex request and automatic paths but does not publish the same retry contract on its GitHub review page. The pilot should record the reaction, final review, elapsed time, and head commit. Someone needs to chase up a missing or outdated review. Neither means the code passed. Test one visible review failure before a wider rollout. Trigger a review where the connected app lacks a needed repository permission, then confirm what the author and release reviewer can see. Record how long recovery takes. A review that silently fails cannot help you catch bugs. ## What Access Do Codex and Claude Code Review Need? Managed Codex and Claude Code Review require a repository connection to their cloud service. Keep review permission separate from branch-write permission. A reviewer can produce useful findings without receiving authority to alter the source branch. OpenAI also allows a follow-up Codex task to prepare a fix when the connection has write access. That is a second decision after a finding has been proved. Review the generated patch, rerun tests, and request a fresh review on the new head. Anthropic’s managed service is unavailable with Zero Data Retention, HIPAA configuration or customer-managed encryption keys. Team and Enterprise administrators choose repositories and a spend cap. If those terms do not fit, Claude Code Action is a different path that runs on a team-owned GitHub Actions runner and calls a configured model provider. A custom action can narrow workflow permissions and network access, but the team then owns the prompt, secrets, event filters, output posting, and provider account. Include that maintenance work when comparing the action's cost with a managed service. ## How Should You Compare Codex and Claude Review Results? Compare Codex and Claude Code Review on the same pull requests, using changes with known outcomes. Include an authorisation path, a migration, a cross-file contract, a generated-file edit, and a routine fix. Keep tests and human approval unchanged so misses remain visible. 1. Request both managed reviews on the same ready commit. 2. Record the head commit, trigger, rules, elapsed time, and spend. 3. Label confirmed defects, wrong findings, repeats, and misses. 4. Check whether each finding names a reachable path and evidence. 5. Make one rules-file adjustment for each service. 6. Repeat the same cases and compare the results. Record the review mode and trigger schedule for each tool. A local Claude pass and a managed Codex review can be legitimate alternatives, but their different permissions, context and costs must be part of the decision. For a direct managed-service comparison, use the same pull requests and trigger schedule. After a finding causes a fix, rerun the relevant test and then the reviewer on the new commit. An old review cannot approve code it never saw. That matters especially when a second review adds delay and a separate usage charge. The [AI code review tools compared](/ai-code-review/comparisons/) guide explains the shared finding labels. ## The Codex and Claude Code Verdict I would keep Codex as the normal GitHub reviewer where its P0/P1 focus and plan allowance fit. I would trial managed Claude Code Review on higher-risk changes to see whether its verification and adjustable depth add useful findings. Paying for both makes sense only if the second review earns its cost and the extra wait. ## Frequently Asked Questions ### Does Claude Code Review block a merge? Claude Code Review does not block a merge or provide a required approval. Its findings do not make the check fail, so teams still need human approval and their own tests or policy checks. ### Can an API key use Codex GitHub review? An OpenAI API key does not unlock native Codex GitHub review; that cloud feature requires qualifying ChatGPT access. An API key can still support Codex in the CLI, SDK, IDE extension, or a custom scripted workflow. ### What should Claude Code Review’s REVIEW.md contain? Claude Code Review’s REVIEW.md should contain repository-specific review rules, such as paths to skip, checks that need project context and the evidence expected with a finding. The managed service reads trusted branch versions of this file rather than the pull request’s changes. ## Read the Full Guides - [Codex review guide](/ai-code-review/codex/) for GitHub and local review setup. - [Claude Code review guide](/ai-code-review/claude-code/) for managed reviews, GitHub Actions and local reviews. - [AI code review comparisons hub](/ai-code-review/comparisons/) for the shared evaluation method. --- ## Codex vs CodeRabbit: Which AI Reviewer Fits Your Team? URL: /ai-code-review/comparisons/codex-vs-coderabbit/ Cluster: AI Code Review > Comparisons Our team has used Codex and CodeRabbit to review pull requests across projects. Codex’s GitHub reviews focus on P0 and P1 issues by default; manual reviews can request P2 findings. CodeRabbit provides a configurable review product with broader Git-provider support and a separate plan. Choose Codex for focused GitHub review through a qualifying ChatGPT plan. Choose CodeRabbit for more review controls and broader Git-host support. We run both here, but that does not mean every team needs two. My [guide to AI code reviewers](/ai-code-review/) covers the wider choices. ## At a Glance | Comparison criterion | Codex | CodeRabbit | | Posted scope | P0/P1 default on GitHub; P2 on request | Configurable review breadth | | Manual trigger | `@codex review` | `@coderabbitai review` | | Rules | `AGENTS.md` | `.coderabbit.yaml` | | Rule hierarchy | Nearest file wins | Global through local sources | | Fix path | Follow-up cloud task | Autofix and agent handoff | | Git providers | GitHub cloud review | Four documented providers | | Entry price | Plus from $20/mo | $24/dev/mo annual, $30/dev/mo monthly | | Review usage | Plan allowance and credits | Plan review limits | | Free access | No cloud review | Summary, CLI, and IDE | Codex limits what it posts to serious issues. CodeRabbit lets you choose a wider or quieter review through profiles and rules. That describes their intended scope, not a measured difference in accuracy or comment volume. ## Evidence from Daily Use Our team has requested Codex and CodeRabbit reviews during releases, each through an explicit standalone comment. Both products work in the same GitHub release process. ## When Should You Choose Codex or CodeRabbit? Codex suits a GitHub team that wants a focused serious-bug review within its existing ChatGPT allowance. CodeRabbit suits a team that needs configurable reviews across different Git hosts. Running both can help, but duplicate findings also mean extra work for the author. One option is to use Codex for serious bugs before release. Its published P0 and P1 filter fits changes where a crash, data loss path, security flaw, or blocking logic error should stop promotion. CodeRabbit can inspect a wider configured policy on selected repositories or paths. That does not make CodeRabbit the “minor comments” reviewer. Its configured tools and path rules can target serious risks that Codex has no product-specific setting for. Codex sets a narrow managed threshold; CodeRabbit gives the team more settings to maintain. We use explicit trigger comments rather than treating either service as approval. Our own workflow shows the two can be sequenced and coexist. Write the intended job beside each trigger during a pilot. “Codex: release-blocking defects” and “CodeRabbit: API boundary plus configured tools” give reviewers a basis for judging overlap. Drop the second reviewer if it still repeats the first reviewer’s findings after you adjust its rules. ## What Do Codex and CodeRabbit Check? Codex reviews changed code with repository guidance, defaulting to P0/P1 findings on GitHub, with P2 available on manual request. CodeRabbit combines AI findings with configurable analysis tools, profiles and path instructions. Neither approach guarantees that every finding is correct. CodeRabbit documents a review layer that combines AI comments with configurable analysis tools, repository knowledge, path instructions, and profiles. The current configuration reference offers `quiet`, `chill`, and `assertive` profiles, with `chill` as the default. The review scope changes with that configuration and the enabled tools. Vendor documentation describes intended scope, while a repository trial reveals actual catches and misses. Use the same labels for both tools when [recording review results](/ai-code-review/comparisons/). ## How Do You Configure Codex and CodeRabbit? Codex uses applicable `AGENTS.md` files for review rules, while CodeRabbit uses `.coderabbit.yaml` and organisation settings. OpenAI's exact heading for review instructions is `## Code Review Rules`, and the nearest applicable file can carry rules for a narrower directory. ``` {`## Code Review Rules - Flag tenant writes without organisation scoping. - Cite the reachable failure path. - Ignore formatting enforced by CI.`} ``` CodeRabbit uses `.coderabbit.yaml` for profiles, tools, path instructions, summaries, automatic review, and related behaviour. The current schema places `profile` and `poem` under `reviews`. The poem setting defaults to `false`. Add a rule only when it helps check a real risk in your code. Start with one repository rule that both reviewers can check, then compare whether each comment cites an affected path and a behaviour. Leave formatting, compilation, and deterministic policy to CI. ## Can Codex and CodeRabbit Fix the Issues They Find? Codex can prepare a fix through a follow-up cloud task, while CodeRabbit offers autofix and coding-agent handoffs. Codex needs a repository connection with write access to change code. You still need to confirm that the reported bug is real and that the patch fixes it. Use a four-step repair rule: confirm the path, add or identify a regression test, review the generated patch, then rerun the relevant checks. Request another review only after the branch reaches the commit the team may merge. Keep review access and write access as separate security choices. A reviewer only needs to read the pull request and post findings. A repair agent can change the branch, so you need to control and check those changes too. Do not let both products patch the same finding during the comparison. That turns one review test into a code-generation contest and hides which explanation was enough for a human to act. Assign repair to the author or one chosen agent after triage. Measure whether the fix saved time. The bug should be gone, the tests should pass and a person should have checked the patch. ## Pricing Codex GitHub review requires a qualifying ChatGPT plan. Plus starts at $20 per month and includes cloud integrations such as automatic code review. Eligible users can buy credits to extend usage. The plan provides a finite allowance, with credits available for additional use. CodeRabbit Essentials costs $24 per developer each month with annual billing or $30 month to month, according to its plans page. CodeRabbit Team costs $48 per developer per month when billed annually or $60 month to month. Advanced costs $72 or $90 respectively. Seats apply to developers who create pull requests, while CodeRabbit publishes separate per-developer review limits for each plan. CodeRabbit Free supports unlimited public and private repositories, though private hosted pull-request review becomes summarisation-only after the 14-day Advanced trial. Code review remains available through the IDE and CLI. Qualifying public open-source projects receive Team features through a separate OSS plan. Published plan terms. | Dimension | Codex review | CodeRabbit | | Billing unit | Plan allowance, then credits | Paid developer seats | | Paid floor | Plus from $20/mo | $24/dev/mo annual, $30/dev/mo monthly | | Higher tier | Pro from $100/mo | Team $48, Advanced $72 per developer/month, billed annually | | Free entry | No cloud review | Summaries, IDE, CLI | Budget for Codex’s shared allowance and additional credits, or CodeRabbit’s paid authors and optional review overages. CodeRabbit’s limits are rolling and adaptive; a busy release day can matter more than the monthly average. Agent and security features may add separate charges. ## Which Git Hosts Do Codex and CodeRabbit Support? Codex managed review supports GitHub and GitLab in beta. CodeRabbit supports GitHub, GitLab, Bitbucket and Azure DevOps. Local or scripted agent review is a separate option with different access and operating requirements. CodeRabbit documents pull-request review on GitHub, GitLab, Bitbucket, and Azure DevOps. Git-host support may settle the choice before you compare the comments. Teams should still verify the required feature on their exact provider because integration depth can differ. For a GitHub team, compare the same pull requests. Record distinct confirmed defects, duplicates, dismissed comments, human triage time, and the plan usage attached to each run. Keep the second reviewer only if its extra findings justify the time spent checking them. ## How Should You Test Codex Against CodeRabbit? Test Codex and CodeRabbit on the same recent pull requests with known outcomes. Include authorisation, migration, contract, generated-code, dependency, and routine maintenance paths. Keep human review and deterministic checks unchanged. 1. Write a distinct job for each reviewer. 2. Give both the same three repository rules. 3. Trigger both on the same ready commit. 4. Record active rules, profile, allowance, and limits. 5. Label confirmed, wrong, repeat, missed, and stale findings. 6. Make one documented rules adjustment, then repeat. Check the live head before accepting a result. If a fix lands after Codex or CodeRabbit posts, that review describes the old code. Rerun the relevant reviewer on the new commit before treating the finding as cleared. A second pass shows whether changing the rules improves the results. Codex may need a narrower `AGENTS.md` rule, while CodeRabbit may need a profile, path filter, or tool setting. Stop after one documented adjustment so the preferred product cannot win through endless tuning. Keep one reviewer unless the second catches real bugs you would otherwise merge. ## The Codex and CodeRabbit Verdict Choose Codex when GitHub, a qualifying ChatGPT plan, and an automatic P0/P1 comment queue match the daily review policy. Choose CodeRabbit when the team needs a configurable dedicated reviewer across its documented Git providers and will maintain the rules and seat budget. We have used both across our projects. For your team, keep both only if the extra reviewer catches useful bugs the first misses. ## Frequently Asked Questions ### Does Codex GitHub review work with an API key? Codex GitHub review does not work through API-key access; the native cloud integration requires a qualifying ChatGPT plan. An API key can still support local or scripted Codex workflows. ### Does CodeRabbit Free review private pull requests? After the 14-day Advanced trial, CodeRabbit Free provides summaries for private pull requests while full code review remains available through the IDE and CLI. Qualifying public open-source projects receive Team features at no cost. ### Should a team run Codex and CodeRabbit together? A team should run Codex and CodeRabbit together only when the second reviewer adds useful findings without excessive duplicate comments. Our team used both in release reviews earlier this year. ## Read the Full Guides - [Codex review guide](/ai-code-review/codex/) for GitHub and local review setup. - [CodeRabbit guide](/ai-code-review/coderabbit/) for profiles, tools, and plan limits. - [AI code review comparisons hub](/ai-code-review/comparisons/) for the shared evaluation method. Facts and prices were checked against the official vendor pages on October 1, 2026. --- ## Greptile vs GitHub Copilot Code Review URL: /ai-code-review/comparisons/greptile-vs-github-copilot/ Cluster: AI Code Review > Comparisons Start with Copilot code review if you already pay for Copilot on GitHub and want to try automated reviews without another vendor. Shortlist Greptile when you need GitLab support, Enterprise self-hosting or a dedicated reviewer that indexes code relationships and works with several repair agents. Both products can inspect code beyond the diff. Greptile prepares a repository graph; Copilot gathers project context during review. Neither approach guarantees better findings, so [choosing between code reviewers](/ai-code-review/comparisons/) should turn on bugs caught, time spent checking comments and total cost. Our [AI code review overview](/ai-code-review/) covers the other contenders. For setup instructions, use the [Greptile evaluation guide](/ai-code-review/greptile/) and [GitHub Copilot review guide](/ai-code-review/github-copilot/). ## The Current Matchup For current product details, see [Greptile's current overview](https://www.greptile.com/docs/introduction), [GitHub's Copilot review overview](https://docs.github.com/en/copilot/concepts/agents/code-review), and both vendors' pricing pages. Check the current plan and availability of preview features before relying on them. | Dimension | Greptile | GitHub Copilot | | Context model | Persistent codebase graph | Per-review project gathering | | Compute path | Vendor indexing service | GitHub Actions runner | | Cold start | Repository indexing | No persistent index | | Provider reach | GitHub and GitLab; more Enterprise hosts | GitHub, Azure preview | | Local rules | `.greptile/` configuration | Instructions and skills | | Review effort | Base, Plus or Apex | Lite or Balanced | | Fix handoff | Several coding agents | Copilot cloud agent | | Billing shape | Seat plus credits | AI Credits plus Actions | ## How Do Greptile and Copilot Use Repository Context? Greptile indexes code relationships before review; Copilot gathers related repository files during its agentic review run. Greptile describes its index as a graph of functions, classes and dependencies. Each pull-request review can use that graph to find related code beyond the diff. The product also supports related repositories through configuration, so it can also read a shared library or SDK. Greptile prepares those repository relationships before review begins. A change to a return type can be matched with an unchanged consumer elsewhere. A new authorisation helper can be checked against the routes that call it. The graph gives the reviewer a prepared map of those links before a specific pull request asks a question. Copilot now explores the project during the review. GitHub documents full-project context gathering, file-exploration tools, repository instructions, agent skills, and MCP servers. Balanced effort sends harder reviews to a higher-reasoning model and spends more AI Credits and runner minutes than Lite effort. GitHub's July 17 update expanded the environment around that gathering. Copilot reads guidance from the head branch, understands more instruction filenames, accepts review-specific setup steps, and runs behind a configurable firewall on hosted runners. Teams can test a review rule within the pull request that introduces it. The two approaches need different upkeep. Greptile needs an index that reflects the current repository and access to every related repository it should read. Copilot needs a runner path, setup file, firewall policy, and working tools for each review that uses agentic features. Try a change that breaks code outside the edited file. If both products find it, check how each explains the bug. Greptile should be able to name the graph relationship, while Copilot should show that its project gathering reached the relevant file or tool. ## What Happens When Greptile or Copilot Lacks Context? Greptile needs its repository index before graph-backed review; Copilot can fall back to a limited review when its agentic runner fails. Greptile’s quickstart says very large repositories may take one or two hours. Once enabled, new pull requests normally receive comments in about three minutes, though queue time and repository size can affect that figure. Copilot starts without a persistent repository indexing wait. Request the reviewer and GitHub starts the review path, usually returning comments in under 30 seconds. Agentic context gathering uses GitHub Actions, so the review environment begins as part of that event rather than existing as a standing repository graph. If the agentic runner is unavailable, Copilot can still return a more limited review. Copilot still generates review comments, while full project gathering and agent tools disappear from the run. You may get comments even though Copilot could not inspect the wider project. Check the runner status as well as the comments. Check index or runner status before treating silence as a clean result. A comment arriving does not prove the expected context loaded. ## How Much Setup Do Greptile and Copilot Need? Greptile needs repository access and indexing; Copilot needs enabled review policies, sufficient credits and a working runner for full-project context. An administrator chooses the runner type, decides whether review can reach the network, and keeps AI Credit and Actions budgets available. Repository owners may also add setup steps for dependencies or generated files. The default hosted-runner path lowers the first setup burden. It can also conflict with an organisation that blocks hosted runners or requires private network access. GitHub supports self-hosted runners for Copilot review, while its default firewall does not currently protect that self-hosted path. Balanced review effort adds another administrative policy decision. Balanced is the current default unless another setting overrides it, and it generally uses more credits than Lite. A ruleset that sends every routine update through Balanced can turn a useful exception into the normal bill. Greptile moves more setup into the vendor connection and repository index. Teams grant repository access, enable selected codebases, wait for indexing, and tune the reviewer. Your team does not have to set up a GitHub Actions environment for each Greptile review. With Copilot, your team manages the review runner. With Greptile, the vendor manages the indexing service. GitHub teams with mature runner policy may find Copilot easier to govern. A small team may prefer Greptile to own the graph service rather than maintain another review-specific runner workflow. ## How Do You Configure Greptile and Copilot Reviews? Configure Greptile through dashboard settings and repository files; configure Copilot through GitHub review policies, supported instruction files and runner settings. Labels, authors, branches, and pull-request text can decide which changes receive automatic review. Draft pull requests are skipped by default, which prevents early generated work from consuming attention before it is ready. Repository-owned Greptile configuration files live under `.greptile/`. Teams can add plain-language instructions, structured rules with severity, ignored paths, referenced files, and related repositories. Nested configuration can narrow a rule to one package instead of making it global. Copilot automatic review is controlled through GitHub rulesets and personal settings. A review can run when a pull request opens, moves out of draft, remains a draft, or receives new pushes. The "Review new pushes" option can multiply events quickly on an active branch. Copilot reads repository-wide and path-specific instructions, plus several common guidance files and relevant agent skills. Review-specific Copilot setup configuration lives in `.github/workflows/copilot-code-review.yml`. GitHub's default firewall can restrict outbound access, and administrators can select hosted or self-hosted runners. Greptile's controls focus on what its standing reviewer should see and say. Copilot's controls reach further into the runtime that gathers context. A platform team may prefer GitHub's runner, firewall, and ruleset model because those controls already have owners. A GitLab team has a shorter decision. Greptile documents GitLab setup and review triggers. GitHub lists Azure DevOps in public preview for Copilot review, while GitLab remains outside the current Copilot review surface list. ## What Code and Data Can Greptile and Copilot Access? Greptile can access the repositories enabled for indexing; Copilot can read repository files and approved tools available to its review environment. Adding related repositories can help with shared contracts, and it gives the service a wider code set. Administrators should list each enabled repository and remove access that does not contribute to a tested review case. The Enterprise plan advertises self-hosting, SSO/SAML, GitHub Enterprise coverage, custom terms, and a data-processing agreement. Ask separately how long Greptile keeps data, where it processes code, how deletion works and what happens to the repository index. Copilot keeps the review path within GitHub's product and runner controls. Its agentic features can read repository context, use MCP servers, and run setup commands. The firewall is enabled by default on hosted runners, and administrators can configure allowed network access separately from Copilot cloud agent. MCP connections can give Copilot access to data outside the repository. A ticket, incident record, or service catalogue entry may help explain a change, while its credentials and returned data become part of the review environment. Approve each connection for a named use case. Ask the same four questions of both products: which code can the reviewer read, which external systems can it call, where derived context persists, and who can change those settings. Check those answers for both vendors, even if your team already uses GitHub. ## Can Greptile and Copilot Fix Findings or Approve Changes? Greptile and Copilot can suggest repairs, hand findings to coding agents and provide approvals when their respective approval settings permit it. Copilot approvals are an opt-in preview; neither a suggested patch nor a default Comment review is an approval. Each Greptile finding can carry "Fix with your Agent." The current product overview lists Claude Code, Codex, Conductor, Cursor, and Devin as destinations. That choice is useful when review and repair belong to different approved tools. Copilot posts a GitHub Comment review with inline suggestions. Developers can accept one suggestion, group several into a commit, or ask Copilot cloud agent to prepare a repair. Replies remain available to human reviewers, while Copilot does not read or answer them in the review thread. GitHub also warns that a re-review may repeat comments that were resolved or downvoted. Track that behaviour when enabling reviews after every push. A repeated false positive can cost more time than the initial review saved. Copilot normally posts a Comment review. Its opt-in approval preview can count towards required approvals when policy allows it. Keep the human approval requirements your team needs, even when Copilot reviews every pull request. Greptile confidence labels and Copilot severity labels are triage aids. Neither label proves that the bug can occur in your code. Require the comment to identify a broken contract, failing state, or test case before a generated fix enters the branch. ## How Much Do Greptile and Copilot Reviews Cost? Greptile charges seats and review credits; Copilot charges AI credits under its plan and can also use billable Actions minutes. Greptile’s review level determines credit consumption, while Copilot usage depends on the review and account policy. Copilot code review is included with paid Copilot plans, then consumes GitHub AI Credits according to model and token use. Project gathering and tools also consume GitHub Actions minutes. GitHub sets one AI Credit at $0.01, though a review's total varies with effort and context. Published plan terms. | Dimension | Greptile | Copilot review | | Billing unit | Seat plus credits | AI credits and minutes | | Paid floor | $30 per seat monthly | Included with paid plans | | Included pool | 50 credits per seat | Shared plan allowance | | Overage | $1 per extra credit | $0.01 per credit | Greptile gives you credits for review; Copilot shares credits with other work. Greptile's seat price reserves a review-credit pool and funds the standing graph service. Copilot review draws from the same plan-level AI Credit system as other Copilot work and adds runner consumption for agentic features. Fifty Base Greptile reviews fit inside one Pro seat's included pool. Twenty Plus reviews need sixty credits, so overage begins even though the pull-request count is lower. Copilot Balanced effort also costs more than Lite, which means raw review counts cannot compare the two bills. Repository count affects the comparison in another way. Greptile lists unlimited repositories on Starter and Pro, yet the active-developer and credit limits still govern use. Copilot bills repository-organisation reviews to that organisation; reviews in personal repositories are billed to the pull-request author. A broad ruleset can therefore increase shared organisational spending. Use the [Greptile credit breakdown](/ai-code-review/greptile/#pricing) and [Copilot review billing section](/ai-code-review/github-copilot/#copilot-review-pricing) for the current per-tool detail. For this matchup, ask whether persistent graph context catches enough extra contract failures to justify a separate seat and vendor. ## Choose a Representative Repository Test the reviewers on a repository where a change in one file can break code elsewhere. Pick a service with shared types, background jobs, or authorisation helpers. A tiny sample repository can make both reviewers look accurate because every relevant file fits into a small prompt. Run three existing pull requests through each product before creating a planted case. Record confirmed defects, repeated comments, missing context, and time spent validating suggestions. Existing work exposes the team's ordinary style and rule set without a test designed around vendor marketing. Then create one contract break whose consumer sits outside the diff. For Greptile, confirm index status and related-repository settings. For Copilot, confirm the runner and setup workflow completed so the agentic path had a fair chance to gather project context. Use a pull request from a developer whose normal Copilot allowance and Greptile seat are both active. This avoids testing an unlicensed-member billing path against a fully provisioned Greptile account. Save the usage report with the finding record so later plan changes do not erase the original cost context. Repeat Copilot once with the agentic runner unavailable in a disposable test setting, then restore it. Compare the reduced review with the full run. This shows whether the team can tell when a comment arrived without the project context it expects. Give Greptile one structural update that forces re-indexing, then test the same consumer relationship again. Check how quickly Greptile’s index catches up with the changed code. Record index delay separately from comment time. Finish with one safe change that resembles the planted defect without breaking the contract. A reviewer that stays quiet on the safe case is more useful than one that comments on every shared function. Choose based on useful bugs caught and time spent checking comments, rather than review length. ## Frequently Asked Questions ### Does Copilot code review read the whole repository? Copilot code review can gather full-project context, but that does not mean it reads every repository file on every review. The pull request remains the subject, while repository exploration supplies related code. If the configured GitHub Actions runner fails or is unavailable, Copilot still returns a reduced review without those agentic context features. ### Must Greptile index a repository before review? Yes, Greptile describes repository indexing as the step that builds its graph of functions, classes, and dependencies. Greptile’s quickstart says very large repositories can take one or two hours to index. New pull requests receive automatic review after the selected repository is enabled and indexed. ### Can Greptile and Copilot run automatic reviews? Yes, Greptile automatically reviews pull requests after repository setup, subject to its trigger filters. GitHub rulesets can request Copilot review when a pull request opens, moves from draft, remains a draft, or receives new pushes. Extra triggers create extra usage, so start with one event. ### Does Copilot review count as an approval? Copilot code review can count as an approval when its opt-in approval preview is enabled by organisation policy. Copilot normally submits a Comment review, which does not satisfy required approvals. Keep a human approval requirement where your team needs independent sign-off. ## My Verdict GitHub Copilot review is the better default for a small or midsize GitHub team with working agentic runners and an existing Copilot budget. You can test its repository context, instructions and automatic reviews before giving another vendor access to your code. Greptile becomes the stronger choice for a midsize or large team when a representative pilot proves that its persistent graph catches cross-file contracts Copilot's review-time gathering misses. It also wins outright for GitLab teams choosing between these two products. The trade-off is an extra seat and standing index in return for context prepared before the review begins. Facts and prices were checked against the official vendor pages on August 8, 2026. --- # Vibe Coding ## Vibe Coding Platform Comparisons URL: /vibe-coding/comparisons/ Cluster: Vibe Coding > Overview & Vibe Coding Vibe coding platform comparisons get confusing because five similar chat boxes hide five different products. Lovable, Bolt.new, v0, Replit, and Base44 can all turn a plain-language request into an app. They differ in the code they expose, the services they manage, and the way a long debugging session reaches the bill. Start with Lovable or Base44 if you want managed services, Bolt or Replit if you want to see and edit the code, and v0 if you ship Next.js on Vercel. Use this page to find a head-to-head guide for your shortlist. Our guide to the [best vibe coding platforms](/vibe-coding/platforms/) covers the broader choices, and the [vibe coding guide](/vibe-coding/) explains the approach. ## Head-to-Head Comparisons The pairwise articles narrow the decision once the broad workflow and billing model are clear. - [Lovable vs Bolt.new vs v0](/vibe-coding/comparisons/lovable-vs-bolt-vs-v0/) compares managed building, browser development, and a Vercel-centered workflow. - [Bolt vs Replit](/vibe-coding/comparisons/bolt-vs-replit/) compares two code-visible workspaces with different project breadth and billing rules. - [v0 vs Bolt](/vibe-coding/comparisons/v0-vs-bolt/) focuses on Next.js and Vercel against a broader JavaScript browser workspace. - [Replit vs Lovable](/vibe-coding/comparisons/replit-vs-lovable/) separates a cloud project editor from a managed web-app builder. - [Lovable vs Base44](/vibe-coding/comparisons/lovable-vs-base44/) examines two managed builders, including credits and portability. - [Base44 vs Replit](/vibe-coding/comparisons/base44-vs-replit/) compares built-in services with a wider coding environment. ## The Five Platforms at a Glance Start with how you want to build: through a managed app builder or an editor that exposes the code. That choice affects how you can fix a problem. | Platform | Main Interface | Paid Entry | Build Meter | | [Lovable](/vibe-coding/platforms/lovable/) | Managed web builder | [$25 monthly](https://docs.lovable.dev/introduction/subscription-plans) | Task-based credits | | [Bolt.new](/vibe-coding/platforms/bolt/) | Browser code workspace | [$25 monthly](https://bolt.new/pricing) | Tokens | | [v0](/vibe-coding/platforms/v0/) | Next.js builder | [$30 per user](https://v0.app/docs/pricing) | Dollar credits | | [Replit](/vibe-coding/platforms/replit/) | Cloud project editor | [$20 monthly](https://replit.com/pricing) | Effort-based credits | | [Base44](/vibe-coding/platforms/base44/) | Managed app builder | [$20 monthly](https://base44.com/pricing) | Message credits | Base44 advertises Starter at $16 per month when billed annually. The same Starter plan costs $20 when billed month to month. ## How Do Vibe Coding Platforms Differ? Lovable and Base44 center on managed app services, Bolt on a browser JavaScript workspace, Replit on a cloud development environment, and v0 on React, Next.js, and Vercel. ### Lovable: Managed Web Apps Lovable combines chat, visual edits, version history, hosting, and a built-in backend. Its [quick-start documentation](https://docs.lovable.dev/introduction/getting-started) describes Lovable Cloud or Supabase for backend work and GitHub for code collaboration. Lovable's FAQ says apps created from May 13, 2026 use TanStack Start. Apps created before that date use React with Vite. Confirm the stack before planning a handoff. ### Bolt: Browser Development Bolt places a JavaScript development workspace inside the browser. Its [official introduction](https://support.bolt.new/get-started/intro-bolt) covers full-stack web apps, websites, and mobile apps through Expo. Code view and source control remain part of the product rather than an export step at the end. Bolt lets you choose the framework and edit individual files. It also means larger projects send more file context to the model. Bolt's pricing page says project file syncing causes most token use, so the cost of a message can rise as the repository grows. ### v0: Next.js on Vercel v0 is centered on Next.js and the Vercel platform. The [full-stack guide](https://v0.app/docs/full-stack-apps) says it can create App Router endpoints and connect databases such as Supabase, Neon, and Upstash. Its defaults point toward React Server Components, Vercel integrations, and Vercel deployment. A team already using that stack can keep its existing tools for databases and hosting. ### Replit: A Broader Project Editor Replit combines Agent with a cloud project editor and a wider set of output types. Its [Project Editor documentation](https://docs.replit.com/learn/projects-and-artifacts/project-editor) lists web apps, mobile apps, presentations, animations, and design artifacts. Plan Mode can produce a task list without changing files, while Build Mode writes the implementation. Replit’s paid modes use effort-based pricing, including paid planning; Free Mode offers no-cost work within its allowance. ### Base44: Built-In Services First Base44 starts with its own authentication, database, hosting, and integrations already attached. The [pricing page](https://base44.com/pricing) includes those core services on Free and separates building activity from live integration activity. The app editor provides source export, and the Builder plan includes GitHub integration. Base44 also has a developer platform and local backend tools. Moving an app also requires checking how to transfer its data and replace its managed services. ## How Should You Compare Vibe Coding Platforms? Evaluate two vibe coding platforms using the same app brief, acceptance checks, and spending limit. Compare the generated result, repair workflow, code and data handoff, and total running cost. A small service-request app is enough to expose frontend, data, and error behavior without turning the trial into a month-long build. > Build a mobile-friendly service-request app with email sign-in. Each user can create a request and see only their own requests. An admin can change status. Show a clear error when saving fails. Add a test account and explain how authorization is enforced. This is an illustrative trial brief, not a reported test. Compare one working pass and one repair pass, then record these observations: - Which requirements appeared in the generated plan before files changed. - Which acceptance checks passed without a corrective prompt. - Whether the app works on a narrow screen and supports keyboard navigation, readable text, and clearly labeled form fields. - How the tool displayed the data model and authorization rules. - Whether a fresh copy of the code ran with documented service settings and secrets. - How much build allowance and runtime allowance the test consumed. This method catches a platform that produces an attractive screen while leaving data access or failure handling unclear. ## The Vibe Coding Comparison Verdict Shortlist Lovable or Base44 for managed services, Bolt or Replit for visible development workspaces, and v0 for Next.js on Vercel. Open the relevant comparison above, then try the same small app in your two favorites to compare the result and cost. ## Frequently Asked Questions ### Which vibe coding platform is best for a beginner? Beginners who want a managed web-app builder should start by comparing Lovable and Base44. Beginners who want to learn from editable code and a terminal should consider Replit. The best fit depends on the app and how much code the builder wants to manage. ### Are free vibe coding plans enough to build an app? Free vibe coding plans can provide enough usage to build a small prototype, but larger apps and repeated fixes may require a paid plan. Building the prototype does not mean the free plan will cover hosting and ongoing use. Check the platform’s daily or monthly limits and the running costs of your app before choosing a plan. ### Can you move a vibe-coded app elsewhere? You can move many vibe-coded apps to another host, but code export alone does not transfer their databases, authentication, or other managed services. Export early, run the project outside the builder, inspect environment variables, and document how data leaves before the app gains users. ### How should you compare vibe coding costs? Compare vibe coding costs with one fixed build brief and four separate costs: the subscription, generation usage, deployed app usage, and outside services. Keep the same screens and acceptance checks for every trial. A flat entry price reveals very little when one platform meters tokens and another meters completed work. --- ## Vibe Coding: The Complete Guide URL: /vibe-coding/ Cluster: Vibe Coding > Overview & Vibe Coding Vibe coding is a way to build software through plain-language requests. You describe a result, an AI system writes or edits the code, and you judge the running result before asking for the next revision. Browser platforms such as Lovable, Bolt.new, v0, Replit, and Base44 put that loop inside one hosted workspace. These products serve founders, small teams, learners, and working programmers who want to turn a specific idea into software. They differ in what they manage, what code you can inspect, which kinds of apps they produce, and what must move if the project leaves the platform. ## What Is Vibe Coding? Vibe coding means directing AI-generated software through prompts and judging the result mainly by how it behaves. The term is also used more broadly for AI-assisted development, although careful code review makes that a different practice. At one end, the builder reads little code and steers through prompts, previews, and error messages. At the other, a programmer asks an agent to draft a feature, then reads the diff, runs checks, and edits the result before it ships. Both ends use generated code, but they place responsibility in different places. Pure vibe coding judges the output mainly by visible behavior. AI-assisted programming also checks the files, data rules, dependencies, tests, logs, and release path. That difference matters because a screen can look correct while the data rules behind it are wrong. A form may submit but expose another account's records. A login page may hide a route in the browser while the server still accepts an unauthorized request. The useful definition therefore describes the loop, whichever brand runs it. A person states intent in everyday language, the AI turns that intent into software, and the person steers by checking each result. The amount of code the person reads can vary, and ownership of the outcome stays with the person either way. ## Why Use Vibe Coding? Vibe coding helps people turn an idea into a prototype they can try before committing to a full build. A founder might use it to test a booking flow. A local team may need a small stock tracker or staff portal. A learner may want to see how a page, server route, and database record connect. A programmer may want the first draft of routine screens and tests. Those goals can share a tool without sharing a workflow. The founder needs a way to test the idea and hand it to someone else. The team needs access rules, backups, account ownership, and a person who will answer for the live service. The learner needs the generated work to stay visible. That means tracing one action from the button to the server and stored record, then explaining what each altered file does. Accepting every revision without reading it may produce an app, but it does not teach how that app works. The programmer gets a different payoff from the same tools. The platform can draft familiar code, connect standard services, or turn a written acceptance check into a first pass. The programmer still owns the review and can reject a large or unclear diff before it spreads through the project. Vibe coding serves these groups best when the first task is small enough to inspect. "Build a customer portal" is too broad. "Let one customer sign in, view only their own request, and add one reply" gives you a small feature to test, including who can see and change each request. ## Who Coined the Term Vibe Coding? Andrej Karpathy introduced the phrase in February 2025 for a loose style of coding in which a person accepts AI output, runs it, and keeps prompting from what appears on screen, and his own example was low-stakes weekend work. Collins Dictionary later named "vibe coding" its 2025 Word of the Year, which fixed the term in wider use. ## How Do Vibe Coding Platforms Differ? Lovable and Base44 emphasize managed web-app building, Bolt exposes a browser development workspace, v0 focuses on Vercel workflows, and Replit combines an agent with a cloud coding environment. Bolt.new starts from a generated JavaScript app. v0 ties generated work closely to Vercel and Git. Replit starts from a cloud coding workspace with Agent alongside files, a shell, Git, and several ways to put an app online. | Platform | Building Interface | What You Need to Manage | | Lovable | Prompt, visual edit, and web-app preview | Web output, services, and repository handoff | | Bolt.new | Prompt, files, terminal, and preview | JavaScript runtime and linked services | | v0 | Generated app, visual edit, Vercel, and Git | Vercel project and team ownership | | Replit | Agent inside a cloud coding workspace | Runtime, deployment, and operating duties | | Base44 | Prompt and managed full-stack app | Managed backend, data, and exit path | v0's team plan is called Plus, and its pricing page and pricing guide both list it at $30 per user per month, so confirm on the vendor page before you budget. Lovable builds web applications and does not generate native iOS or Android projects. Its FAQ names two phone-install paths: make the published site a Progressive Web App, or place the web app in a Capacitor wrapper for store submission. A wrapper still needs signing, store accounts, device checks, privacy details, and release work. The [platforms hub](/vibe-coding/platforms/) compares all five in more detail. The [comparisons hub](/vibe-coding/comparisons/) is more useful after you have narrowed the list to two or three tools that can build your app. ## Define the Project Before the Tool Start a vibe coding project by naming its user, one task, and the result that would count as success. A personal reading list, for example, needs a way to add a book, mark it finished, and keep the list after a refresh. Leave accounts, payments, and shared records out of a first experiment unless they are essential. Set a spending limit and decide whether the result is a private prototype or something other people will use. List any data and services it needs. Use made-up records while you work out whether the basic flow is useful. ## How Does the Vibe Coding Process Work? The vibe coding process follows a prompt, generation, test, and revision loop. You describe one outcome, let the builder generate it, try the result, and request a specific correction before adding more features. 1. Describe the task: For a reading-list example, ask for a page that adds book titles, marks them finished, and keeps the list after a refresh. 2. Inspect the result: Add a title, try an empty entry, mark a book finished, and refresh the page to check whether the list persists. 3. Request one correction: Describe the failed action and the behavior you expected, rather than asking the builder to fix everything. 4. Save a working version: Use the platform's history or Git before the next change. Treat stored data as a separate backup concern. This is an illustrative exercise, not a reported product test. For choosing between builders, use the same small task in each candidate and compare the result, repair effort, and usage. The [vibe coding platform guide](/vibe-coding/platforms/) explains the product differences. ## What Can You Build with Vibe Coding? Vibe coding can build prototypes, personal tools such as reading lists, and simple internal apps. They let a team test wording, order, screen flow, and demand before committing to a long build. Use false data, keep the audience small, and delete the prototype if its job was only to answer a question. Personal tools can also fit well when one person owns the data and can live with downtime. A bookmark list, trip planner, reading log, or household tracker can cause less harm if it fails than a customer service that handles accounts and money. Internal tools need more care than the word "internal" suggests. A staff dashboard may contain customer records, sales figures, private files, or access to other systems. It still needs server-side login checks, narrow permissions, an owner, and a tested way to take it offline. A public app raises the bar again for the builder and any reviewer. Account separation, abuse controls, logging, backups, data removal, billing, uptime, and incident steps become part of the product. Generated code can still be used, but the release process must treat it like any other code. The fit gets weaker when the app has many linked states, strict response-time targets, unusual hardware, regulated data, or a long expected lifespan. The first visible version may still arrive quickly, but review and operating work grow with the stakes. Reversibility gives the most useful dividing line between those cases. If a broken revision can be rolled back, test data can be thrown away, and no outsider is harmed, a loose prompt loop may be acceptable. If a mistake can expose records, charge money, or block a business process, move to written checks and qualified review. ## How Does Vibe Coding Differ from Agentic Coding? Vibe coding describes a person steering generated software mainly through prompts and visible results; agentic coding describes a tool that can carry out tasks such as editing files and running commands. A coding agent can be used with either careful code review or a hands-off vibe coding workflow. Karpathy's February 2025 post defined vibe coding by attention: you accept each change without reading the diff and keep prompting from whatever appears on screen. Anthropic uses the other word for capability, calling Claude Code "an agentic coding tool" that reads your codebase, edits files, runs commands, and works with your development tools. | Question | Vibe Coding | Agentic Coding | | What the term describes | How closely the person checks | What the tool can do alone | | Code gets read | Rarely, if ever | In careful use, as a diff | | Main check | The preview looks right | Tests, builds, and review | | Typical tools | Lovable, Bolt, Replit, v0 | Claude Code, Codex, Cursor Agent, Copilot agent mode | The two practices overlap in both directions. You can vibe code with a terminal agent by approving every change unread, and you can use a browser builder carefully by reading each change and testing each permission. Replit even calls its builder Agent, which shows how far the two labels have blurred. Anthropic's own best-practice guide for Claude Code still expects a check on the agent's work. Without a test or build the agent can run, the guide warns, "looks done" is the only signal available and you become the verification loop. The narrower meaning of vibe coding involves accepting code without reading it. A workflow that reviews code and runs checks is better described as AI-assisted development, even when it uses the same builder. Where vibe coding stops and programming starts, including DHH's view from the Lex Fridman Podcast, gets a fuller treatment in the [vibe coding vs agentic coding section](/vibe-coding/is-vibe-coding-real/#vibe-coding-vs-agentic-coding) of our real-programming article. ## What Are the Risks of Vibe Coding? Vibe coding can produce working screens with incorrect access rules, exposed secrets, or fragile code. A working preview does not establish that the application is safe. Veracode's 2025 report tested 80 coding tasks across more than 100 language models. It reported an OWASP Top 10 flaw in 45 percent of the test cases, and security scores stayed flat even as newer models wrote more syntactically correct code. Wiz reported in September 2025 that one in five organizations it studied had systemic exposure tied to vibe-coded apps. The recurring faults included browser-side password checks, secret keys shipped to the browser, open database tables, and private tools published without login protection. Put authentication and authorization checks on the server side. Authentication proves who sent a request, and authorization decides whether that person may read or alter a specific record. Hiding a button leaves the route open, because a person can still call it directly. Keep secrets in a server-side secret store, never in browser code or a prompt pasted into a public service. Use test credentials and false records while building. Before launch, scan packages, inspect database rules, and test every account type against records owned by another account. Recovery needs its own check before launch. Source history can restore code, but it may not restore database rows, uploaded files, domains, or outside service settings. Back up each part, restore it into a test space, and record who has permission to do the same during an incident. Stop automated edits when records disappear, accounts cross boundaries, a secret may have leaked, or payment records disagree with the payment provider. Preserve logs and the last working revision. Bring in a security or platform specialist before another broad prompt overwrites the evidence. ## Can You Move a Vibe-Coded App to Another Platform? A vibe-coded app can move to another platform when its source code, data, and required services can be exported or replaced. Exporting a repository alone may leave the app dependent on the original builder. The running system may also depend on database records, uploaded files, secret variables, user accounts, scheduled jobs, domains, payment settings, email senders, analytics, and a vendor's hosted backend. Write an inventory before calling code "exported." For each item, name the owner account, backup method, move process, and test that proves the copy works. Ask a second person to start the project from a clean copy without using the original builder's session. Check which kind of mobile app you need before choosing a builder. A responsive web app runs in a phone browser. A Progressive Web App can be installed from the browser and may gain some offline and device features. A wrapped web app uses a shell such as Capacitor. A native app uses a mobile stack such as React Native with Expo. Each path leads to a different release process and set of device features. Lovable documents responsive web apps, PWA installation, and Capacitor wrapping. Replit documents an Expo path for iOS and Android. Bolt also has an Expo route, while a v0 or Base44 project should be checked against the current vendor docs and the required phone features before selection. Store release adds work outside the builder. Apple and Google accounts, certificates, app identifiers, screenshots, privacy forms, permission text, physical-device checks, and review remain part of the release. A store button does not remove those duties. Before handing over the app, ask the next maintainer to run it, complete its main task, and restore a working version. Write down any missing setup steps or account access they need. ## Vibe Coding Verdict Use vibe coding for a small prototype, personal tool, or internal task that you can test and restore if something breaks. It works best when you can check the result before other people depend on it. Do not treat a polished preview as proof that an app is ready for customers. Once the project holds private records, takes payments, runs a business task, or needs a store release, require written acceptance checks, source history, tested backups, account separation, and a qualified review. Choose a platform that supports the app you need and that you or a developer can maintain. Try the same small task in your shortlisted tools, then compare the result, running cost, and recovery options. If you are still weighing whether any of this holds up in practice, our article on [whether vibe coding is real programming](/vibe-coding/is-vibe-coding-real/) takes the skeptical case seriously and answers it with evidence you can check. ## Frequently Asked Questions ### Is vibe coding the same as AI code completion? Vibe coding differs from code completion: completion suggests code while you write, whereas vibe coding uses prompts to generate and revise software based on the running result. Completion helps with authorship, while vibe coding moves more work toward directing, checking, and revising generated output. ### How does vibe coding differ from copying code from Stack Overflow? Vibe coding generates and revises software interactively, while copying a Stack Overflow answer gives you a fixed snippet to adapt by hand. A vibe coding tool can use project context, revise several files, run commands, and respond to the result. That wider reach is useful, but it also means one vague request can alter more code than a copied snippet would. ### Is it cheating to use vibe coding? Using vibe coding is cheating when it violates the applicable rules or misrepresents how the work was produced. For paid work, follow the agreement with the client or employer. For schoolwork, follow the course and assignment rules. Using an allowed tool is different from hiding its use, claiming work you cannot explain, or submitting generated output where the rules ban it. ### What should I prepare before opening a vibe coding platform? Before opening a vibe coding platform, prepare a brief naming the user, one task, its required data, and checks that show whether the task works. Add a spending limit and name who will own the code, accounts, domain, and stored records. This short brief keeps the first build focused and gives you a clear way to check the result. --- ## Is Vibe Coding Real Programming? URL: /vibe-coding/is-vibe-coding-real/ Cluster: Vibe Coding > Overview & Vibe Coding Vibe coding can count as programming when a person defines a computer’s behavior through natural-language instructions. That is our view, not a settled definition. Some developers reserve “programming” for work that includes understanding the implementation. Either way, a working AI-generated preview does not prove that an app is ready for other people to rely on. Our standard is that someone can state the intended behavior, show evidence that it works, and take responsibility when it fails. A Starting Point for Non-Coders Who Want to Try You can start before you know a programming language. Keep the first project private and reversible, and save a working version before each new feature. If a prompt breaks the app, restore the last good version and narrow the next request to one failure. Stop when the problem involves another user's data, payments, or secrets, or when two focused repair attempts fail. Keep the safe version while a qualified reviewer checks that path. ## What Does Vibe Coding Mean? [Vibe coding](/vibe-coding/) usually means describing software in natural language and allowing an AI system to generate a substantial part of the implementation. The builder evaluates a preview, reports errors, requests changes, and may inspect less code than a conventional development workflow requires. The phrase comes from Andrej Karpathy's February 2025 post, which described accepting whatever the model produced until you "forget that the code even exists." He framed it as a way to build throwaway weekend projects. Current tools cover a wider range of behavior. A session can include planning, file-by-file review, direct edits, automated tests, source control, and deployment. The label now spans many degrees of delegated implementation. The degree of delegated work changes what must be checked. A builder who accepts every generated change and refreshes the preview is doing different work from a builder who writes acceptance criteria, reviews diffs, tests permission boundaries, and diagnoses failures. Both may use prompts as the main input. ## Why Do Developers Disagree About Vibe Coding? Developers disagree about vibe coding because they use different definitions of programming and different standards for a finished product. The debate combines two questions: - Classification: does directing a machine through language count as programming? - Responsibility: can the person shipping it verify and maintain the system? Classification is mostly a dispute about where programming begins. Programming already includes layers of delegation: compilers translate languages, frameworks supply behavior, libraries hide algorithms, and cloud services hide infrastructure. Natural-language generation adds one more layer of delegation. Quality can be measured through observable behavior and repeatable checks. The app either enforces its access rules or it does not. A clean deployment either follows documented steps or depends on hidden workspace state, and a recovery either preserves data or loses it. Responsibility still has an identifiable human or organizational owner. A generated file cannot approve a launch, accept legal obligations, respond to an incident, or explain a product decision. The person or organization releasing the software keeps those duties. ## How Does Vibe Coding Differ from Agentic Coding? Vibe coding describes how a person delegates and checks the work; agentic coding describes a tool that can plan, edit files, and run commands. An agent can support either unreviewed generation or carefully reviewed development. Because one term describes the software and the other describes the builder's attention, a single session can be either. A developer who approves every change a terminal agent makes without opening the diff is vibe coding with an agentic tool. A founder who reads each change a browser builder proposes and tests each permission is doing careful work on a platform sold for vibe coding. | Practice | Who Checks the Code | Typical Setting | | Vibe coding | No systematic code review | Throwaway and personal projects | | Agent-accelerated development | A person who reads diffs and runs checks | Shared codebases | | Hand-written code | The author or a reviewer; checks still vary | Any project | The companies building coding agents describe the careful end in their own documentation. Anthropic's best-practice guide for Claude Code says that without a check the agent can run, "looks done" is the only signal available, and you become the verification loop for every mistake. It tells users to have the agent show evidence, such as test output, instead of asserting success. That careful practice now goes by names such as agentic engineering, the phrase in the title of DHH's Lex Fridman episode. "Vibe coding" stays most useful as the name for the unreviewed end, because a term that covers every use of AI in programming stops telling you anything. DHH, the Ruby on Rails creator and 37signals CTO, drew the boundary more sharply on episode #501 of the Lex Fridman Podcast, published August 26, 2026. Asked whether agentic engineering should simply be called programming, he said no, because programming to him means understanding the constructs of a language, such as loops and variables. His definition of the other side was blunt: "vibe coding, if we define it here, is you tell an agent to build software for you. You do not look at the implementation." He added: "That, to me, is what separates vibe coding from programming or, let's say, agent-accelerated development." His comparison was a CEO who hires programmers, tells them what to build, and sells the software that comes out. He doesn't think most people would call that CEO a programmer, and he said the same applies to the vibe coder. DHH is no purist about the practice itself. He says he has several vibe-coded projects of his own, including a C++ writing app whose code he deliberately never looked at. He also argues that product judgment, deciding what the software should do and for whom, matters more than programming skill once an agent handles the implementation. Where other people depend on the result, he checks more. On the Quattro release of his Omarchy Linux distribution, none of the shipped code was written by hand, yet he says he reviewed its overall shape and read every line of the critical model-layer code. At 37signals, letting designers vibe code features in February 2026 produced pull requests that together "destroyed the architecture of the system," and the team cleaned it up by hand. DHH's definition and Anthropic's guidance point at the same working test for any session, which is whether anyone reads the result before it ships. If someone reviews the diff and can explain the change, the work is what DHH calls agent-accelerated development. If nobody does, it's vibe coding whatever the tool calls itself, and the responsibility test from the previous section applies with full force. ## A Spectrum from Experiment to Production The spectrum from experiment to production describes rising consequences rather than fixed technical stages. A spectrum from Experiment to MVP to Production Experiment MVP Production private and reversible real users and feedback trust and operations low consequence high consequence Verification grows with the number of people who depend on the software and the harm a failure can cause. A private experiment can tolerate rough setup, incomplete tests, and a manual reset. Its purpose may be learning whether an interaction feels useful. The builder can discard the result with little external cost. An MVP serves real people or informs a real product decision. It needs explicit acceptance checks, basic access control, reliable data handling, and enough observability to interpret feedback. A tiny audience still creates obligations when users supply personal information. Production adds operating conditions that a private experiment can avoid. Backups, incident response, audit logs, rate limits, dependency maintenance, accessibility, privacy, and rollback plans become part of the software. A generated interface is one part of that system. ## Does Writing a Prompt Count as Programming? A prompt can perform part of the programming work when it specifies behavior, rules, and failure conditions. A vague request such as “build an app” leaves most of those decisions to the generator. Consider the instruction "build a customer portal." It leaves the generated system to guess which records a customer may see, how identity is established, what staff can change, and what happens when a request fails. The prompt contains a product category and very few requirements. A stronger brief identifies observable behavior before any files change: > Customers sign in by email and can view only invoices linked to their account. Staff can issue a credit after confirming the amount. Reject duplicate submissions, log every credit, and preserve the form when the payment service times out. That brief still needs design and implementation decisions. It gives the generator and the reviewer a shared target. The shift from wish to acceptance criteria is programming work because it shapes the machine's behavior. Replit's own Agent guidance lists five habits for builders: be specific, plan the work, add context, review and test, and use checkpoints. Its Plan Mode produces a task list for approval before Build Mode changes any files. Even a vendor selling prompt-driven building treats planning and review as part of the job. ## Do You Have to Write Code Yourself to Be Programming? You do not have to type every line yourself to do programming work. Choosing requirements, configuring existing components, and checking how they behave can be substantive work even when libraries or AI supply the implementation. Software work already includes assembling code written elsewhere. A developer can install a library, configure a framework, call a hosted API, or compile a higher-level language without personally authoring the implementation below it. Each abstraction shifts attention toward choices made at another layer. An AI model may create many files from one instruction, leaving the person directing it unable to explain a particular function. That makes review harder because there is more unfamiliar code to check before anyone can rely on it. Two records provide more useful evidence than a line count: - The requirement that explains why the behavior exists. - The test or observation that shows the requirement was met. These records connect intent to implementation and implementation to evidence. They also let another person challenge a result without debating whether the original builder deserves an identity label. ## What Changes When Other People Use a Vibe-Coded App? When other people rely on a vibe-coded app, the builder must account for access, stored data, failures, and ongoing support. A private prototype becomes a service with users who can lose work or information. A scheduled job needs an owner and a monitoring signal. A paid feature needs billing recovery and a way to resolve disputes. Revenue is one signal of dependence, although it is never required for software to matter. An internal accessibility tool, a family scheduling app, or a volunteer database can carry real consequences without charging anyone. The number of users also gives an incomplete measure. One person may place sensitive health information into a small tool, while thousands may use a disposable public calculator that stores nothing. Data sensitivity, reversibility, and the cost of failure tell more than audience size alone. Write one concrete consequence statement before any public launch: > If this application fails, these people lose this capability or data. The owner will detect the failure through this signal and restore service through this procedure. A builder who cannot complete that statement has found the next piece of engineering work. The answer may justify keeping the project private, reducing the data it stores, adding a manual fallback, or asking a qualified reviewer to inspect the risky path. ## Who Is Responsible for AI-Generated Software? The person or organization publishing AI-generated software remains responsible for reviewing, operating, and maintaining it. Delegating code generation does not assign those duties to the model. OpenAI's May 2025 Codex launch post says users should manually review and validate all agent-generated code before integration and execution. GitHub's Copilot code review documentation says Copilot won't spot every problem and tells teams to supplement its feedback with a human review. Those cautions also apply to prompt-to-app builders and hosted agents. A successful preview verifies one route through one environment. It cannot establish that another user lacks access, a secret stayed server-side, retrying an action will not duplicate it, or a restore works. Ownership becomes visible through the actions a team can prove: - The requirements name users, permissions, data, and failure behavior. - Generated changes receive a review proportionate to their risk. - Tests cover acceptance criteria and important abuse cases. - Deployment and rollback both follow written, repeatable instructions. - A named person or team monitors and maintains the application. A project can satisfy those conditions while AI writes most of its files. A hand-written project can fail every one of them. The number of lines a person typed tells you little about how well the app is maintained. ## Does Vibe Coding Make Software Safe? Vibe coding does not establish that software is safe. Generated applications still need checks for exposed credentials, data access, authentication, and unintended public access. Wiz's September 2025 vendor research on vibe-coded apps reported that one in five organizations in its observed population had vibe coding platforms exposed to risk. The article names client-side authentication, exposed secrets, open databases, and publicly reachable internal applications. That figure is limited to Wiz's telemetry and methodology. The concrete failure modes are useful because each can become an acceptance check. | Risk | Verification | Failure Signal | | Client-side login | Browser bundle review | Password in the code | | Exposed secrets | Bundle and repo scan | Usable credential | | Open database | Anonymous request | Readable records | | Public internal app | Unauthenticated URL | Loaded workspace | An earlier Wiz article on rules files for safer vibe coding shows one way to place security constraints into the generation context. Rules can keep the same instruction in every generation. Tests and review still need to prove that the resulting application follows them. ## What Skills Do You Need to Check AI-Generated Code? Checking AI-generated code requires skills in testing behavior, reading changes, reviewing permissions, and diagnosing failures. Generating a plausible implementation and proving that it meets its requirements are different tasks. Prompting skill helps a builder describe a candidate. It cannot recognize every requirement that the builder forgot to state. Domain knowledge matters where the interface hides taxes, health records, financial authorization, or tenant isolation. Learning can happen alongside a low-stakes project. As builders learn to spot failures, read generated code, and design better tests, they can take on more demanding projects. They should still be able to check the parts that other people will rely on. ## How Can You Judge Whether a Vibe-Coded Project Works? Judge a vibe-coded project by whether it meets its stated purpose, protects its data, and can be run and repaired by its owner. Check the following areas at a depth that matches the consequences of failure: - Purpose: the project solves a named problem for a named user. - Behavior: acceptance checks cover success and failure. - Access: authorization is tested across user roles. - Data: collection, retention, export, deletion, and recovery are documented. - Supply chain: dependencies and external services are known and maintained. - Operations: deployment, monitoring, rollback, and incident ownership are repeatable. - Handoff: another person can run and understand the project from its repository. An experiment may intentionally leave several areas incomplete. Write that boundary down beside the project's saved version. An MVP with test users needs stronger access and data evidence. A production service should have answers across the list at a depth that matches its risk. This evidence test avoids two common errors in opposite directions. A polished screen receives no automatic credit for hidden behavior. A rough internal tool receives no automatic rejection when its limited purpose, access, and recovery are clear. Keep the evidence with the repository so future changes can be judged against the same expectations. ## Frequently Asked Questions ### Is vibe coding the same as AI code completion? Vibe coding is different from AI code completion: completion usually suggests a small continuation while the developer directs the implementation. Vibe coding delegates a larger outcome through natural-language instructions and generated changes. The boundary can blur when a person alternates between prompts, direct edits, tests, and review. ### Can vibe-coded software ever be ready for production? A vibe-coded application can be ready for production after its behavior, security, data handling, accessibility, operations, and recovery paths have been checked to the standard its users require. The generation method supplies no shortcut through those checks. The finished app must meet those standards regardless of how its code was written. ### Do you need to learn programming before vibe coding? A beginner can use a vibe coding tool before learning a language in depth. Shipping consequential software requires skills for decomposing requirements, reading failures, testing behavior, reviewing security, and operating the result. Those skills can grow alongside a project, provided the project's stakes stay within the builder's current ability to verify it. ### Does a public repository prove a vibe-coded app works? A public repository does not prove that a vibe-coded application works; it shows that its files are available. Runtime evidence, repeatable setup, tests, and a clear owner provide stronger evidence that the app works, protects data, and can be maintained. ## Does Vibe Coding Count as Real Programming? In our view, vibe coding counts as programming when natural-language direction defines and changes a computer’s behavior. The decisive standard for a real project is responsibility: someone can state the intended behavior, produce evidence that it works, and respond when it fails. Some experienced developers draw that line much tighter than this. DHH keeps the word "programming" for work that rests on understanding the language itself, and a session where nobody reads the result before it ships stays vibe coding in Karpathy's original sense, whatever tool runs it. Generated code changes the route to implementation while leaving quality, security, ownership, and production readiness to be proved. --- ## Best Vibe Coding Platforms (October 2026) URL: /vibe-coding/platforms/ Cluster: Vibe Coding > Overview & Vibe Coding Lovable is my first recommendation for a non-coder building a web app, while Base44 suits a small prototype with less service setup. Bolt, v0, and Replit fit different development workflows. This guide compares these vibe coding tools by their strengths, prices, and limitations to help you choose an app builder for your project. ## What Are Vibe Coding Platforms? Vibe coding platforms turn a plain-English request into a running web application inside a hosted browser workspace. Lovable, Bolt.new, v0, Replit, and Base44 differ on access to source code, databases, user accounts, version history, hosting, and pricing. For the underlying workflow and its limits, read our [guide to vibe coding](/vibe-coding/). ## Which Vibe Coding Tool Should You Choose? Lovable is my first suggestion for a non-coder testing a web-app idea; Base44 suits a small record-based tool. Bolt suits developers who want browser-based files and a terminal, v0 suits React and Next.js work, and Replit suits learners who want a full cloud coding workspace. | Platform | Best fit | Main trade-off | | Lovable | A managed web-app prototype | Running costs and moving data to another host | | Base44 | A small internal tool with records and login | Check the paid plan needed for GitHub and export | | Bolt.new | Web development with visible files and a terminal | Larger projects can consume more tokens per request | | v0 | React and Next.js work on Vercel | Include deployment and connected service costs | | Replit | Learning or building in a full cloud IDE | Account for agent usage and published-app costs | ## How Much Do Vibe Coding Tools Cost? Standard entry paid plans for these five vibe coding tools cost $20 to $30 a month on monthly billing: Base44 Starter and Replit Core cost $20, Lovable Pro and Bolt Pro cost $25, and v0 Plus costs $30 per user. Usage and connected services can add to those subscriptions. Cheaper allowances also come with conditions worth reading. Replit's Free Mode picks the model for you, and Bolt Forge trades a bigger allowance for sharing anonymized sessions with model trainers. Neither condition is a deal-breaker for a throwaway prototype, but both matter for client work. The platform profiles below link to detailed guides. Use the [head-to-head comparisons](/vibe-coding/comparisons/) once you have narrowed your shortlist. ## Why Use an AI App Builder? AI app builders help you turn an idea into a clickable prototype or interactive demo quickly. That is what I like about Lovable, v0, and Bolt.new. Our team uses these builders rarely, and I would not ship a production app on their output alone. Choose a builder that supports the kind of app you need, then try a small task before paying for a larger build. ## Platform Profiles ### Lovable Best for: A managed web-app prototype. Lovable builds React and TypeScript web apps through chat and a visual preview. It is my first suggestion for a non-coder who wants to test a web-app idea without setting up each service separately. Lovable Cloud and Supabase can handle the backend, but moving the finished app elsewhere may still require moving its data and services. Pricing: Lovable Pro costs $25 a month and adds 100 monthly credits, plus five daily build credits and 20 monthly Cloud credits. Business costs $50 a month, and plans charge by credits rather than seats, so extra members share one pool. The [Lovable platform guide](/vibe-coding/platforms/lovable/) explains hosting, security, and publishing. ### Bolt.new Best for: Web development with visible files and a terminal. Bolt gives web developers a browser workspace with source files, a terminal, and a running preview. Its Node.js-compatible environment uses StackBlitz WebContainers. Choose it when you want to inspect or edit the generated code as you build; larger projects can consume more tokens with each request. Pricing: Bolt Pro costs $25 a month and starts at 10 million tokens, with no daily cap and unused tokens rolling over. The Teams plan costs $30 per member per month. Forge has separate usage and model-training terms, and promotional access can change. The [Bolt platform guide](/vibe-coding/platforms/bolt/) explains token usage and agent options. ### v0 by Vercel Best for: React and Next.js work on Vercel. v0 suits React and Next.js projects that will run on Vercel. It connects generated interfaces with databases and GitHub, so a team can develop the app beyond its first preview. Include hosting and connected services in your budget rather than treating the builder subscription as the full running cost. Pricing: the [v0 pricing page](https://v0.app/pricing) lists Free with seven messages a day and $5 in one-time credits for new users. Plus costs $30 per user a month, and includes $30 of monthly credits. The Business plan costs $100 per user a month. The [v0 platform guide](/vibe-coding/platforms/v0/) explains credits and hosting. ### Replit Best for: Learning or building in a full cloud IDE. Replit combines Agent with a cloud development environment containing a terminal, files, and version history. It suits learners and builders who want to work in several languages without setting up a local environment. The extra control also means more development tools to learn, and published-app costs need to be budgeted alongside Agent usage. Pricing: Replit Core costs $20 a month, or $18 per month, billed annually, with $20 toward the most powerful models. Free Mode covers everyday chat and building within a plan allowance that resets every five hours. Replit Pro, at $100 a month or $90 per month, billed annually, adds $100 toward models and up to 15 collaborators. The [Replit platform guide](/vibe-coding/platforms/replit/) explains the IDE and publishing flow. ### Base44 Best for: A small internal tool with records and login. Base44 suits small internal tools that need records, user accounts, and forms with little service setup. It manages data, login, email, files, and analytics in one platform. The main trade-off is dependence on those managed services. Exporting code does not automatically move the whole app, and GitHub integration requires a higher paid plan. Pricing: Base44 Starter costs $20 a month, or $16 per month, billed annually, with 100 message credits and 2,000 integration credits. Builder costs $50 a month, or $40 per month, billed annually, and adds GitHub integration and model choice. The [Base44 platform guide](/vibe-coding/platforms/base44/) explains its two credit balances and app export. ## How Do Lovable, Bolt, v0, Replit, and Base44 Differ? Vibe coding platforms differ in how much control they give you over files, hosted services, and deployment. Replit and Bolt keep files and development tools visible, while v0 connects generated work to Vercel projects and Git branches. Lovable and Base44 put more of the first backend setup behind managed services. The second difference is what leaves the platform. Source code can move through GitHub or an export path, while hosted records, secrets, authentication settings, scheduled jobs, and provider accounts may need separate migration work. The platforms also measure usage differently. Bolt meters tokens and now tracks Forge usage separately, v0 uses dollar-denominated credits with per-model token rates, and Replit splits work between Free Mode and paid credits. Lovable funds building and live services through one credit balance, while Base44 separates message credits from integration credits. ## Which AI App Builders Can Build Mobile Apps? Replit and Bolt.new can build mobile apps through Expo, while Base44 packages a web app in a native shell. Lovable builds web apps rather than native mobile projects. A mobile-friendly website and an app submitted to a phone app store are different outputs. | Platform | Phone output | Store path | | Rork | SwiftUI and Kotlin apps | App Store tools built in | | Anything | React Native via Expo | App Store submission | | Replit | React Native via Expo | App Store from Publishing | | Bolt.new | React Native via Expo | Expo build on your machine | | Base44 | Web app in native shell | Store files, Builder plan | | Lovable | Web app only | PWA or third-party wrapper | Rork documents Swift apps for Apple devices and Kotlin apps for Android. Anything, Replit, and Bolt use React Native with Expo. Rork's public pricing page did not expose current plan details during this review, so confirm its subscription before choosing. Replit's [mobile guide](https://docs.replit.com/build/mobile-app) covers Expo Go testing and App Store submission. Base44 takes a lighter route: its store build runs your published web app inside a native web view. Most content changes then reach users without a new store release, but downloading the store files needs the Builder plan. Lovable says plainly that it does not generate React Native projects. Lovable and v0 both offer phone apps of their own, but those apps build the same web projects as their desktop versions. Every store route still needs a paid Apple Developer account for iPhone and a Google Play developer account for Android. Apple's Xcode has its own agentic coding features too, but it is a developer tool, so it belongs with AI coding tools rather than here. If native iOS work is the goal, the [Swift guide](/programming/swift/) covers the language those apps run on. ## How Should You Compare Vibe Coding Tools? Compare vibe coding tools by giving each the same small task and checking the working result, correction cost, and export path. A client tracker can test a form, saved records, and access rules. The steps below are a suggested exercise, not a claim that we ran a new benchmark. 1. Create a blank project and record the starting credit or token balance. 2. Paste the same request: create a client tracker where signed-in users add contacts and can read only their own records, with a mobile layout and status filter. 3. Test the result with two accounts and record the first build cost. 4. Request one specific change, then record the ending balance and changed files. 5. Connect GitHub or export the code, run it outside the builder, and list every service that remains attached to the platform. Keep the prompt, plan, test date, and before-and-after usage balance with your notes. Those details make the comparison repeatable and distinguish your results from vendor claims. ## Questions to Answer Before Paying Ask what consumes the allowance, when it resets, and whether unused credit expires. Then ask whether editing, hosting, database traffic, file storage, and in-app AI share that allowance or create separate charges. Check which plan provides GitHub, custom domains, private publishing, team roles, and training controls. The cheapest paid tier may support more generations while leaving the export or access feature you need on a higher plan. Check the training setting on day one as well. Bolt's model-training switch covers content created from October 7, and v0 only turns training off by default from its Business plan upward. ## How Do You Recover When a Vibe Coding Prompt Breaks Your App? Recover an app broken by a vibe coding prompt by saving any work you need, restoring the last working code version, and checking the main user flow again. Code restoration may not restore database records. If the failure involves lost data, payments, or exposed secrets, address that separately before resuming generation. | Platform | Recovery control | Important limit | | Lovable | Version History revert | Code, not database data | | Bolt.new | Restore this version | Database stays current | | v0 | Restore generation | Generated versions only | | Replit | Rollback here | Review database option | | Base44 | Revert or Version History | Data restores separately | Base44 data backups keep seven days on Elite and 30 days on its Business and Enterprise plans. Replit production databases start with seven days of history; Pro and Enterprise can extend retention to 28 days. Code rollback and production-data recovery remain separate operations. These controls can restore an earlier code version without manual source edits. They do not settle a security or data failure. If the problem exposed private data or affected payments, have a qualified reviewer check the affected feature before reopening the app. ## Can You Export an App from an AI Builder? You can move exported source code off a vibe coding platform, but a working app may also need its database, uploaded files, secrets, domain settings, and connected services. Code ownership alone does not establish that every managed service can move with it. Connect GitHub before a large build when the plan allows it. Create one outside commit, bring it back into the platform, and confirm the sync direction. Then run the exported project in a clean environment. Before moving the app, list what the new host will need: - Repository and default branch - Runtime and build commands - Environment variables and secret owners - Database schema and record export - Domains, storage, jobs, and payment providers Base44's eject command, for example, copies schemas and creates an empty database. Lovable can now export a Cloud database, but the export arrives as an emailed download, capped at 5 GB and once per project a day. A successful source export can still leave the application incomplete. ## When Should You Move from an App Builder to a Coding Assistant? Move from a hosted app builder to a coding assistant when control over the repository, custom infrastructure, or a developer-led review process becomes more useful than the builder’s managed workflow. Moving the files does not fix problems in the generated code. Consider making that move when: - You spend more time repairing broad AI rewrites than building features. - The project is large enough that important files fall out of the agent's context. - You need pull requests, protected branches, or multiple developers working independently. - Hosted usage is harder to predict than running the project in infrastructure you control. [Cursor](/ai-coding-tools/cursor/) is one code-level option after Bolt or Lovable. It runs against a local codebase and fits a normal Git workflow, but its plans still have usage allowances and model-dependent limits. Compare the current included usage rather than assuming an editor subscription is unlimited or flat. [Claude Code](/ai-coding-tools/claude-code/) is a terminal-native option that can inspect, read, and modify a local codebase. It becomes relevant after export when the project already has a command-line workflow and needs repository-level work outside the hosted builder. The [Best AI Coding Tools](/ai-coding-tools/) ranking covers that next step. ## Compare Them Side by Side The [comparisons hub](/vibe-coding/comparisons/) places shortlisted candidates beside each other on pricing, code access, and project requirements. The [Lovable vs Bolt vs v0](/vibe-coding/comparisons/lovable-vs-bolt-vs-v0/) comparison covers those three managed browser builders. The [vibe coding hub](/vibe-coding/) explains the wider category, its workflow, and its limits. ## Vibe Coding Platform Verdict Lovable is the clearest first test for a non-coder building a managed web app, while Base44 removes more service setup for a small record-based prototype. Bolt fits a web developer who wants visible files and a browser terminal, v0 fits React and Next.js teams on Vercel, and Replit fits learners or multi-language builders who want a full cloud IDE. For a phone app, start with Rork when native Swift or Kotlin matters, and with Replit when one Expo codebase for both platforms is enough. Whichever you pick, treat the result the way we do: a prototype or interactive demo that proves the idea, then a real review before anything reaches production. ## Frequently Asked Questions ### Are vibe coding platforms safe for production? Vibe coding platforms do not guarantee production-safe apps; generated code and configuration need a security review before launch. Test login and access controls, protect secrets, review database rules, scan dependencies, and check payments in test mode. Apps handling sensitive records, money, or business-critical actions should receive an independent professional review before launch. ### Can I try vibe coding tools for free? Lovable, Bolt.new, v0, Replit, and Base44 offer free entry points with different usage and publishing limits. Bolt caps free accounts at 300,000 tokens a day and 1 million a month. v0 allows seven messages a day and advertises $5 in one-time credits for new users. Replit grants daily Agent credits and one published app, Lovable grants five build credits a day, capped at 30 a month, and Base44 allows five message credits a day, up to 25 a month. Pricing was checked against official vendor pages on October 17, 2026. Temporary offers and account-specific allowances can change. --- ## base44 vs Replit: Which App Builder Should You Choose? URL: /vibe-coding/comparisons/base44-vs-replit/ Cluster: Vibe Coding > Comparisons Base44 and Replit can both turn a written request into a running application, but they place the complexity in different hands. Base44 manages a defined web-app stack. Replit exposes a cloud development environment where the builder can see code, commands, version control, and deployment choices. Choose Base44 for a managed web app with fewer technical choices, and Replit for visible code, Git, and deployment control. The difference shows up after the first demo, when somebody must change a data model, recover an account, understand a charge, or move the project. For the broader approach, read our [vibe coding overview](/vibe-coding/). The [comparison directory](/vibe-coding/comparisons/) covers other platform pairings. ## Should You Choose Base44 or Replit? Choose Base44 for a managed business web app with fewer setup decisions, or Replit when editable code, a terminal, and flexible architecture matter more. | Need | Base44 Fit | Replit Fit | | Managed business web app | Strong | Possible with more choices | | Visible code and shell | Secondary developer path | Core workflow | | Two-way GitHub work | Builder plan and higher | Built into workspace flow | | Data exit | Separate from eject | Separate from code export | | Fewer setup decisions | Better aligned | More tools to learn | | Custom architecture | Constrained by platform | Better aligned | Try a small app before committing to either platform. The [Base44 guide](/vibe-coding/platforms/base44/) and [Replit guide](/vibe-coding/platforms/replit/) provide the product-specific detail. ## What Is the Main Difference Between Base44 and Replit? Base44 bundles the app’s services into a managed builder, while Replit gives you a cloud coding workspace with more control over files, commands, and deployment. Base44 begins with an application description and supplies a managed frontend, backend, database, authentication system, file handling, and platform integrations. Its developer documentation identifies a React and Vite frontend with Base44-managed services behind it. The product decides much of the architecture so the user makes fewer infrastructure choices. Replit begins with a cloud workspace. Its Agent can plan and build from natural language, but the resulting files sit beside an editor, shell, Git tools, and deployment controls. The user can change code directly, use Agent again, or bring in a developer without leaving the environment. Neither structure is automatically safer or more maintainable. Base44 reduces setup while making the app more dependent on its services. Replit offers more control, but somebody must understand how to run, secure, deploy, and save the code. | Question | Base44 | Replit | | Main interface | Managed app builder | Cloud development workspace | | Backend | Base44 managed services | Project-selected architecture | | Direct code work | Developer tools and GitHub | Editor, shell, and Git | | Main tradeoff | Fewer choices, more platform dependence | More choices, more ownership | ## What Does Base44 Manage for You? Base44 manages database records, authentication, serverless functions, storage, email, and real-time updates for your app. A builder can describe an internal tool, customer portal, directory, or workflow without first selecting separate database and authentication vendors. That integration is useful when the application fits the platform's model. It also means data access, backend functions, and built-in services should be evaluated as Base44 capabilities, not as generic code that can be moved unchanged to any host. Base44's credits also distinguish building activity from some live-app activity. Message credits pay for prompts, while integration credits cover documented operations such as built-in email, file processing, image generation, and AI-agent or language-model calls. A quiet demo and an active app can therefore have different cost patterns. ## What Can You Control in Replit? Replit lets you inspect and edit project files, run commands, use Git, and choose how the app is deployed alongside its AI Agent. Replit Agent has Plan and Build modes. Plan Mode analyzes the project and proposes work without editing files, while Build Mode makes changes and creates checkpoints. The workspace keeps those changes visible, which makes it possible to inspect the implementation rather than judging only the preview. Replit's version-control documentation supports Git workflows, GitHub import, branches, and repository synchronization. Agent checkpoints are useful recovery points, but they do not replace meaningful commits and an external repository when a project needs review or long-term ownership. The workspace can support projects outside Base44's managed web-app shape because the architecture lives in project files. That flexibility is not permission to choose an unfamiliar stack at random. A non-coder should prefer a common, documented path that a local developer can support. Replit also offers several deployment types. Static suits files that do not need a server. Autoscale, Reserved VM, and Scheduled deployments address different runtime patterns. Selecting the wrong type can create unnecessary cost or unreliable behavior, so the workload should drive the choice. ## How Do You Edit Apps in Base44 and Replit? Build and revise apps in Base44 through its chat and managed app controls; in Replit, combine Agent requests with direct file and terminal work. For either platform, the safest first request defines one user, one task, and one success condition. Ask for a staff member to create and update an appointment before adding customer accounts, reminders, payments, analytics, and multiple locations. In Base44, review the entities, permissions, and integrations that the prompt created. In Replit, review the plan, changed files, commands, and checkpoint. The tools you inspect differ, but test the same behavior in both: perform the task, refresh, try invalid input, and confirm unauthorized users are blocked. Keep each revision small and name the expected result. “Add a confirmation message after an appointment is saved” is reviewable. “Make the app production ready” is not, because it hides security, reliability, accessibility, privacy, and operational requirements inside an undefined phrase. Both platforms support visual changes without manually editing code. Base44’s [Theme and Edit controls](https://docs.base44.com/Building-your-app/Design) handle overall styling and individual elements. Replit’s browser-only [Visual Editor](https://docs.replit.com/design/visual-editor) applies simple text, color, and spacing changes without AI credits; complex changes passed to Agent use credits. ## Is Base44 or Replit Cheaper? Base44 Starter and Replit Core have the same $20 monthly entry price, but their usage allowances differ. Base44 is cheaper on annual Starter billing; neither is universally cheaper for a running app. Base44 lists Free with 25 monthly message credits and 100 integration credits. Starter costs $20 monthly or $16 per month, billed annually, with 100 message credits and 2,000 integration credits. Builder costs $50 monthly or $40 per month, billed annually, with 250 and 10,000 respectively. Base44 also lists Pro at $100 monthly or $80 per month, billed annually, and Elite at $200 monthly or $160 per month, billed annually. Credit allocations increase by tier. Base44's credit documentation says prompt cost is dynamic, and unused paid monthly credits do not roll over. Replit lists Starter as free, Core at $20 monthly or $18 per month, billed annually, and Pro at $100 monthly or $90 per month, billed annually. Core includes $20 in monthly credits, and Pro includes $100. Those credits cover paid Agent work and cloud-service usage; Free Mode offers no-cost Agent work within its allowance rather than a fixed number of prompts. | Entry Option | Listed Price | Budget Variable | | Base44 Free | $0 | Messages and integrations | | Base44 Starter | $20 monthly | 100 messages, 2,000 integrations | | Replit Starter | $0 | Daily Agent and cloud limits | | Replit Core | $20 monthly | $20 paid Agent and cloud credits | Run the same budget experiment on both products: build one representative feature, fix one defect, and operate it with test data for several days. Record build consumption and runtime consumption separately. Do not extrapolate from a landing page to an application with users, files, email, or AI calls. ## Can You Move Base44 and Replit Apps Elsewhere? Both Base44 and Replit expose code through Git workflows, but moving a working app also requires migrating data and replacing any services tied to the original platform. Base44 provides two-way GitHub synchronization on Builder and higher plans. Its documentation says local changes must reach the main branch to sync back into the app, and publishing remains a separate Base44 action. The connected repository improves code access without turning the backend into an ordinary self-hosted service. The Base44 CLI also has an eject command. It creates a separate project and new app identifier, copies frontend code and backend resource definitions, and leaves the original unchanged. Existing database records are not copied, so a live migration needs a separate data-export and import plan. Replit projects expose their files and standard Git workflows. Moving the repository still does not transfer environment variables, deployment settings, databases, domains, or accounts at external services. Portability must be tested as a running system, not inferred from the presence of source files. ## How Do Base44 and Replit Handle Deployment? Base44 keeps deployment within its managed product. Replit asks the user to select a deployment type and charges runtime usage against available credits. In both cases, publishing is a technical action, not proof that the service is ready for customers. Replit's Starter plan allows one published app, and its documentation says that app can go down after 30 days. Core or Pro is required for broader publishing capabilities. Treat free hosting as a learning or validation path rather than an availability commitment. Base44 live usage can consume integration credits. Identify which features depend on those credits and what users see when the balance is exhausted. A graceful failure message and manual fallback are better than discovering that a critical workflow silently stopped. ## Can Base44 and Replit Build Mobile Apps? Base44 can package a published web app in an app-store wrapper on Builder and higher plans, while Replit can generate React Native apps through Expo. Both still need developer accounts, device testing, and store approval. Building from a phone is a separate capability. “Can this be used on a phone?” can mean a responsive web app, a mobile development interface, or a native application distributed through stores. These are different deliverables with different testing and release requirements. Replit offers a native mobile client for starting Agent work, reviewing projects, and editing from iOS or Android. Replit's product page says native mobile-app creation and store submission should be completed through the web product on a computer. The phone client does not remove store tooling. Base44 builds web applications that can be designed responsively. If App Store or Google Play distribution is mandatory, confirm the supported packaging and maintenance route before committing. A web page that looks good on a phone is not automatically a signed native binary. For non-coders, the practical decision starts with required capabilities. If a phone browser is sufficient, test a responsive web app on physical devices. If push notifications, offline storage, platform purchases, or store discovery are required, involve a mobile developer before selecting the platform. ## Is Base44 or Replit Better for Learning? Replit is the stronger fit for learning how code and development tools work; Base44 is the simpler starting point for learning to model a business workflow without managing as much infrastructure. Base44 minimizes the number of development concepts a builder must handle. That helps someone focused on a business workflow, but the project still needs written explanations of entities, roles, integrations, and operating procedures. Hiding implementation does not remove the need for ownership. Replit makes code and tooling visible, which creates more opportunities to learn. Learning is not automatic. A useful routine is to ask for an explanation of one changed file, trace one request from interface to storage, and document one error before asking Agent to fix it. ## When Do Base44 or Replit Users Need Help? Base44 or Replit users should get technical help when a problem involves lost records, cross-account data access, payment disputes, compromised credentials, Git conflicts in security-sensitive files, or a failed migration. Preserve logs and a working snapshot. Repeated automated fixes can overwrite evidence and enlarge the incident. For Base44, seek help when managed-service behavior or data migration exceeds the documented controls. For Replit, seek help when shell commands, dependency changes, deployment networking, or Git history are unclear. ## The Verdict Choose Base44 for a conventional managed web app when reducing technical choices matters more than choosing and managing backend services. Choose Replit when visible code, broader architecture, Git-centered collaboration, and deployment choice justify the added operational responsibility. Use the trial to check running costs, user permissions, and how you would move the app elsewhere. ## Frequently Asked Questions ### Is Base44 cheaper than Replit? Base44 Starter and Replit Core both cost $20 month to month. Base44 Starter is cheaper on annual billing at $16 per month, compared with $18 for Replit Core. Those prices are not equivalent allowances, so compare Base44 message and integration credits with Replit Agent and cloud usage for one representative project. ### Can Base44 code move to GitHub? Base44 app code can sync with GitHub in both directions on Builder and higher plans. It also provides an eject command that creates a separate project with frontend code and backend resource definitions. Ejection does not copy existing database records, and the copied project uses a new Base44 app, so data migration remains separate work. ### Does Replit include hosting? Replit includes hosting through Static, Autoscale, Reserved VM, and Scheduled deployments, with usage charged against plan credits. Deployment runtime is usage-based and draws from plan credits before additional charges. Match the deployment type to the workload, configure spending limits, and test sleep, scaling, and scheduled behavior before promising availability. ### Is Base44 or Replit better for a non-coder? Base44 is the better starting point for a non-coder who wants fewer technical choices for a conventional web app. Replit is a better fit when the builder wants to learn from visible code or work with a developer inside the same coding workspace. Pricing and code-export details were checked against official sources on October 17, 2026. --- ## Bolt vs Replit: Which App Builder Should You Choose? URL: /vibe-coding/comparisons/bolt-vs-replit/ Cluster: Vibe Coding > Comparisons Bolt and Replit are browser-based app builders with AI agents. Bolt focuses on JavaScript applications, while Replit provides a coding workspace that supports a broader range of languages and hosting options. Choose Bolt for a new JavaScript web or Expo app on a guided path, and Replit when you need other runtimes, phone access, or explicit deployment options. The choice decides what can run, how costs appear, and who must understand the deployment. See our [vibe coding guide](/vibe-coding/) for the wider approach and the [platform comparisons](/vibe-coding/comparisons/) for other matchups. ## Should You Choose Bolt or Replit? Choose Bolt for a new JavaScript or Expo app on a guided path; choose Replit for broader runtimes, a phone client, and more explicit deployment options. | Requirement | Bolt Fit | Replit Fit | | New JavaScript web app | Direct product path | Supported with more choices | | Non-JavaScript backend | External service required | Better aligned | | Phone-based development | Mobile browser not fully supported | Native client available | | Expo mobile app | Documented workflow | Documented workflow | | Deployment choice | Bolt Cloud and integrations | Several deployment types | | Git-centered learning | Available | Central to workspace | Compare them with a small project that resembles the app you want to build. The [Bolt guide](/vibe-coding/platforms/bolt/) and [Replit guide](/vibe-coding/platforms/replit/) cover each product in more depth. ## What Is the Main Difference Between Bolt and Replit? Bolt centers on JavaScript web and Expo apps in a browser workspace; Replit offers a broader cloud development environment with more runtime and deployment choices. Bolt combines chat, project files, a terminal, and a live preview around website, web-app, and Expo mobile generation. Bolt Cloud can provide databases, hosting, and domains. The product is designed to move from a description to a running JavaScript application without local setup. Replit combines Agent with a cloud editor, shell, version control, and deployment products. Agent can propose a plan, edit files, run the project, and create checkpoints. Direct code work is part of the normal workspace. For a non-coder, Bolt's narrower application path can reduce initial choices. Replit supports more kinds of projects, but somebody must choose the technical setup and maintain the running app. “More flexible” is useful only when the team can support the extra decisions. | Dimension | Bolt | Replit | | Center of workflow | Generated JavaScript app | Cloud development workspace | | Managed services | Bolt Cloud and integrations | Workspace and deployments | | Planning | Plan Mode | Plan Mode | | Primary billing unit | Tokens | Dollar-denominated credits | ## Which Languages Do Bolt and Replit Support? Bolt supports JavaScript-based projects with Node.js backends, while Replit can run a broader range of language and framework configurations. Bolt's browser-support documentation draws a clear boundary around JavaScript backends. It does not run PHP or Python backends in the Bolt environment. A Bolt application can call an externally hosted service, but that creates a second deployment and a second operational owner. Replit workspaces can be configured around the project's chosen language and framework. That broader scope helps with scripts, APIs, data work, and applications that do not fit Bolt's JavaScript path. It also makes dependency, runtime, and deployment choices more visible. Use the required backend as an early filter. If a project already depends on a Python package or a specific runtime, confirm it with a minimal execution test before generating the interface. Do not rebuild a working backend solely to fit a preferred AI builder. If the project is a new JavaScript web app, compare the service boundaries instead. List the database, authentication, files, email, payments, and scheduled jobs. Mark which product supplies each item and which external account owns it. ## How Do Bolt and Replit Plan and Recover Changes? Bolt and Replit both offer planning before implementation and recovery controls for saved changes. Bolt's Plan Mode normally analyzes without editing, although its documentation says the first plan in a new project creates initial structure. Replit's Plan Mode is read-only and can be followed by Build Mode when the plan is accepted. A useful plan is an agreement about behavior. It names the user, the action, the stored data, the failure states, and the acceptance checks. Ask the agent to flag decisions that require a business or security owner. Bolt provides version history, and Replit Agent creates checkpoints. Neither removes the need for Git when changes must be reviewed, compared, and preserved outside the workspace. Name recovery points after verified behavior so a non-coder can restore the right state. Build one vertical slice before adding a second feature. For a booking tool, create, view, edit, and cancel one appointment. Refresh between actions, use invalid data, and verify roles before adding reminders, payments, reporting, or multiple locations. Bolt and Replit also let you change visible elements directly. Bolt’s [Select tool](https://support.bolt.new/building/visual-edits) previews changes free, then uses tokens when you save them to code. Replit’s browser-only [Visual Editor](https://docs.replit.com/design/visual-editor) makes simple direct edits without AI credits; changes routed through Agent use credits. ## Is Bolt or Replit Cheaper? Replit Core has the lower monthly subscription price at $20, compared with Bolt Pro from $25. Total cost depends on paid generation, hosting, and connected services rather than the subscription alone. Bolt Free includes one million monthly tokens and a 300,000-token daily limit. Pro starts at $25 per month with ten million monthly tokens and no daily limit. Teams starts at $30 per member per month, while Enterprise uses custom pricing. Bolt's paid unused tokens can roll into the next month and remain valid for two months in total. Project size, selected agent, file syncing, and repeated fixes affect consumption. A token allowance therefore cannot be converted into a reliable feature count. Replit lists Starter as free, Core at $20 monthly or $18 per month, billed annually, and Pro at $100 monthly or $90 per month, billed annually. Core includes $20 in monthly credits and Pro includes $100. Credits cover paid Agent work and cloud services rather than a fixed prompt total. Free Mode provides no-cost Agent work within its allowance. | Paid Entry | Listed Price | Included Meter | | Bolt Pro | From $25 monthly | 10M monthly tokens | | Bolt Teams | From $30/member/mo | Team token allowance | | Replit Core | $20 monthly | $20 monthly credits | | Replit Pro | $100 monthly | $100 monthly credits | A responsible budget starts with measured project costs. Record one representative build, one debugging cycle, and a week of test deployment. Repeat after the codebase grows, because later work can use more context or runtime resources than the initial scaffold. Set a written experiment budget before enabling extra usage. Include the subscription, AI work, deployment runtime, storage, database activity, and any third-party services. Stop at the limit and review the billing dashboards rather than letting an unfinished feature determine the next charge. ## How Do Bolt and Replit Hosting Costs Differ? Bolt hosting costs depend on Bolt Cloud plan limits and any external services, while Replit charges deployment usage against available credits first. Replit offers Static, Autoscale, Reserved VM, and Scheduled deployments, so the workload affects which hosting option you need. Replit's deployment pricing is usage-based and applies available credits first. Bolt's application may also depend on paid plan limits or external integrations. Configure budget alerts and test what users see when a service reaches a limit. Free plans suit exploration and carry no availability promise. Replit's Starter documentation limits publishing and says its single published app can go down after 30 days. Check current terms before sharing a free deployment as a permanent public service. ## Can Bolt and Replit Connect to GitHub? Both Bolt and Replit can import and synchronize GitHub repositories. Bolt leaves branch merging to GitHub; Replit exposes Git operations in its development workspace. Check the branch and current build before asking either agent to modify an existing project. Replit supports Git, GitHub imports, branches, and repository synchronization from the workspace. Agent checkpoints help with recovery, but an external repository remains the clearest place for another developer to review and continue the work. Code export does not move the running system. Secrets, databases, uploaded files, OAuth settings, domains, deployment configuration, payment products, and email accounts require separate documentation. Test the repository on another machine before calling either product portable. A good handoff lets a second person clone the repository, supply documented test credentials, start the project, and complete the main user flow. If the original builder must explain missing steps from memory, the project still depends on that person. ## Can You Use Bolt or Replit from a Phone? Replit lets you start Agent work, review projects, and edit from its iOS and Android app; Bolt does not fully support mobile browsers for building apps. Use a supported desktop browser for substantial Bolt work. Bolt's browser-support documentation says mobile browsers are not fully supported. Use a supported desktop Chromium browser for substantial Bolt work. A phone can still view a deployed responsive application, which is different from using the phone as the development environment. ## Can Bolt and Replit Build Native Mobile Apps? Bolt and Replit both document Expo-based development for native iOS and Android applications. Bolt requires the first prompt to state that the project is a mobile app. Replit separates work in its mobile client from the computer-based path used to build and submit native apps. Store submission needs more than generated screens. Apple and Google require developer accounts, signed builds, identifiers, store listings, screenshots, privacy information, testing, and review. Device permissions, offline behavior, deep links, and notifications also need physical-device checks. If store distribution is not essential, a responsive web app may be simpler to operate. Make that product decision before generation. Converting a web experience into a dependable native release is a separate project. ## How Does Collaboration Work in Bolt and Replit? Bolt collaborators share a project chat, and Bolt documents that only one prompt is processed at a time. The project owner controls GitHub and certain integrations. Replit's paid plans add collaborator and parallel-Agent allowances, with limits varying by plan. Parallel activity can increase throughput, but it also increases coordination risk. Assign ownership by feature or file and require changes to enter a shared repository. Two agents changing authentication or schema code at the same time should be treated as a conflict. ## What Should You Check Before Launching a Bolt or Replit App? Before launching a Bolt or Replit app, test login and permissions with separate accounts, check recovery, and review its hosting costs. Verify sign-up, sign-in, password recovery, session expiry, account deletion, and every role. Try to access another user's record through direct URLs and request identifiers. For payments, use the provider's sandbox and official test methods. Cover success, decline, cancellation, retry, duplicate submission, refund, and webhook failure. Keep live keys out of prompts and under organization-controlled accounts. Escalate lost data, cross-account access, compromised keys, payment discrepancies, or unexplained deployment changes to a qualified person. Preserve the last working state and logs. Repeated agent fixes can destroy the evidence needed to diagnose the problem. ## How Can You Compare Bolt and Replit Fairly? Compare Bolt and Replit by giving both the same small app brief, checking the same requirements, and recording build and hosting usage separately. An illustrative equipment-checkout app can expose the important differences: staff add items and approve loans, while borrowers see only their own requests. Check saved data after a refresh, invalid input, and access from a second account. Then make one correction and restore an earlier version. Finish by opening the repository outside the builder and identifying any services that must move separately. This tests the handoff as well as the first preview. ## The Verdict Choose Bolt when the project is a new JavaScript web or Expo app and the team values a guided generation path with integrated Bolt Cloud services. Choose Replit when broader runtime choice, a development-centered workspace, phone access, and explicit deployment options justify the extra setup decisions. Compare the cost of building and running one small feature before committing. ## Frequently Asked Questions ### Is Bolt cheaper than Replit? Bolt Pro has a higher monthly starting price than Replit Core: $25 versus $20. Bolt meters tokens, while Replit credits cover paid Agent work and cloud services. Either can cost more for a particular project, so compare generation and deployed usage as well as subscriptions. ### Can Bolt and Replit both use GitHub? Bolt and Replit both support GitHub: Bolt can import, create, and synchronize repositories, although branch merging happens on GitHub; Replit supports Git, GitHub imports, branches, and repository synchronization from its workspace. In both products, verify that secrets, databases, domains, and deployment settings have separate ownership and recovery documentation. ### Can Bolt and Replit build native mobile apps? Bolt and Replit both document Expo-based native iOS and Android development. Bolt requires the first prompt to identify a mobile app, while Replit says store submission should be completed from its web product on a computer. Either route still needs developer accounts, signed builds, physical-device testing, privacy details, store assets, and platform review. ### Is Bolt or Replit better for learning? Replit is the better fit for learning a broad development workflow with a shell, Git, and deployment choices. Bolt suits learners concentrating on JavaScript apps in a more guided environment. In either product, trace one request, inspect the changed files, and explain the result before adding more. Pricing, supported technologies, and Git workflows were checked against official sources on October 17, 2026. --- ## Lovable vs base44: Which App Builder Should You Choose? URL: /vibe-coding/comparisons/lovable-vs-base44/ Cluster: Vibe Coding > Comparisons Lovable and Base44 build business web apps from plain-language descriptions and manage many of the services behind them. When [comparing app builders](/vibe-coding/comparisons/), check how you edit the app, use GitHub, pay for live usage, and move code and data elsewhere. Choose Lovable for a new web app built through visual iteration, and Base44 when a tightly managed backend matters more than a clean exit. For the broader approach, see our [guide to vibe coding](/vibe-coding/). ## Should You Choose Lovable or Base44? Choose Lovable for visual web-app iteration and Base44 for a business app whose backend you want the platform to manage. Existing code, store packaging, and backend ownership can change that choice, as the table shows. | Requirement | Lovable Fit | Base44 Fit | | New web app with visual iteration | Strong | Supported | | Managed backend model | Available through services | Built into the platform | | Existing repository import | ZIP rebuild, gradual rollout | Check sync workflow | | Store-distributed app | Capacitor wrapper | Builder store packages | | Complete backend departure | Service-specific migration | Backend rebuild required | Before choosing, complete one representative workflow, run role tests, connect GitHub, inspect the bill, and rehearse an exit. The [Lovable guide](/vibe-coding/platforms/lovable/) and [Base44 guide](/vibe-coding/platforms/base44/) provide more product detail. ## How Do Lovable and Base44 Differ? Lovable emphasizes visual web-app iteration, while Base44 centers its managed database, authentication, and business-app services. Both can generate full-stack web apps; the difference is how much backend configuration and migration work stays tied to each platform. Base44 also generates full-stack web applications, but its managed backend sits at the center of the product. Base44 documents entities, authentication, functions, storage, email, and real-time features within its own services. The builder makes fewer backend-vendor choices at the start. Both can suit a dashboard, portal, directory, scheduler, or internal workflow. The deciding questions arrive after generation: who owns the data, how code joins an existing repository, what live actions consume credits, and what must be rebuilt to leave. | Dimension | Lovable | Base44 | | Primary output | Full-stack web app | Managed full-stack web app | | GitHub | Project-created repository sync | Builder plan two-way sync | | Backend exit | Depends on chosen services | Separate migration required | | Store app route | Capacitor wrapper | Builder store packages | ## What Can You Build with Lovable? Lovable can build web apps such as dashboards, portals, directories, and internal tools through chat and visual editing. Its managed services can support login and stored data, but those behaviors need testing beyond the screen preview. Lovable Cloud can cover runtime services, and the current credit documentation describes Build, Cloud, and AI usage. Use the balances shown in the workspace when planning a project. Do not translate a build-credit total into a promise about hosting or language-model activity. Lovable's GitHub integration creates a new repository and synchronizes the project. A separate [code-import feature](https://docs.lovable.dev/features/projects/import-project) can rebuild a repository ZIP into a new TanStack Start project. Availability is rolling out by account; database and user migration are separate. Paid plans allow code download, and GitHub provides a durable copy. Neither action automatically moves domains, secrets, databases, authentication settings, or external service accounts. Those dependencies need their own ownership and exit checklist. ## What Does Base44 Manage for You? Base44 manages database records, authentication, serverless functions, storage, email, and real-time updates for your app. Its frontend uses React, React Router, Vite, Tailwind CSS, and shadcn/ui. This reduces setup while keeping backend services tied to Base44. Base44's developer tools provide more code access than a closed visual builder. GitHub sync and the CLI make it possible to inspect and change frontend code. They do not convert managed backend capabilities into ordinary infrastructure that can be moved unchanged. This reliance on Base44 can suit a managed business tool. It becomes a concern when a contract requires a specific database host, complete offline data export, self-hosting, or an established backend. Confirm those conditions before users or records make migration harder. Lovable’s [Preview toolbar](https://docs.lovable.dev/features/preview-toolbar) supports inline text changes, element selection, drawings, and comments. Base44’s [Theme and Edit controls](https://docs.base44.com/Building-your-app/Design) adjust global styling and individual elements. Both offer more than chat. Try changing a heading and button style to see which editing approach suits you. ## Is Lovable or Base44 Cheaper? Base44 Starter has the lowest monthly subscription at $20, followed by Lovable Pro at $25 and Base44 Builder at $50. Builder adds GitHub synchronization and store-file downloads. Their credits measure different work, so the cheapest entry plan may not cover the features or usage you need. Lovable's plan documentation also lists monthly Cloud and AI grants, and higher credit tiers are available. Current credit documentation describes usage across building, cloud services, and AI. Check which balance pays for a planned action instead of treating every credit as interchangeable. Base44 Free lists 25 message credits and 100 integration credits. Starter costs $20 monthly or $16 per month, billed annually with 100 message and 2,000 integration credits. Builder costs $50 monthly or $40 per month, billed annually with 250 message and 10,000 integration credits. Base44 also lists Pro at $100 monthly or $80 per month, billed annually, and Elite at $200 monthly or $160 per month, billed annually. Its credit guide says prompt cost can vary. Paid monthly credits reset without rollover, so unused capacity should not be treated as stored project value. | Entry Plan | Listed Price | Primary Allowance | | Lovable Pro | $25 monthly | 100 credits | | Lovable Business | $50 monthly | 100 credits plus controls | | Base44 Starter | $20 monthly | 100 messages, 2,000 integrations | | Base44 Builder | $50 monthly | 250 messages, 10,000 integrations | ## Can Lovable and Base44 Use Existing Code and GitHub? Lovable can rebuild an existing code ZIP when Upload code is available and create a new GitHub repository; Base44 supports two-way GitHub sync on Builder and higher plans. Syncing a generated app does not by itself import an existing application's data or backend. Base44 enables two-way GitHub synchronization on Builder and higher plans. Local changes sync through the main branch, while publishing remains separate. Test the connection with a disposable project before several people edit the app. For both products, establish repository ownership under the organization. Require protected review for authentication, data access, payments, and deployment configuration. A generated change should not bypass the same controls that apply to manually written code. Test a clean clone on another machine. Supply documented test environment variables, start the project, and complete the main flow. The exercise will reveal missing services and setup knowledge that a successful Git synchronization cannot prove. ## Can You Move Lovable and Base44 Apps Elsewhere? You can move code from Lovable and Base44, but moving the running app requires separate backend and data work. Lovable's migration depends on its connected services. Base44's eject command creates another Base44 app with code and resource definitions, without copying existing database records. That makes ejection a code and configuration path only. A departure needs data extraction, transformation, import, file transfer, identity mapping, downtime planning, and validation. Test that process before the database contains irreplaceable records. Lovable's exit work depends on which backend and integrations the project uses. GitHub or downloaded code covers the repository, but every attached service has separate data and configuration. List each service and verify its export, backup, billing, and account-recovery procedures. ## How Do Lovable and Base44 Security Checks Compare? Lovable provides an automatic Quick scan on publishing for database access, dependencies, and related checks, plus a deeper code-context scan that can be run manually. Base44 offers a security scanner on all plans, with code-vulnerability scanning on Builder and above. Lovable also states that its security tools do not replace professional review. A scanner can identify classes of problems, but it cannot approve the business rules, privacy decisions, or every path through the application. Apply the same standard to Base44. Test every role with separate accounts, attempt direct access to other users' records, review server-side functions, and verify that deletion and export behave as promised. Managed infrastructure does not make an untested permission model safe. Base44’s [scanner](https://docs.base44.com/Setting-up-your-app/running-a-security-scan) checks permissions, exposed secrets, and backend access, among other issues. Review proposed fixes and retest affected features. Its publishing warnings do not block release, so a successful publication is not evidence that security checks passed. ## Can Lovable and Base44 Publish Mobile Apps? Base44 can package its published web app for app stores, while Lovable points to an external wrapper such as Capacitor. Both create web-based mobile experiences rather than native screens. Their own mobile builder apps are separate from the apps you generate. Lovable's FAQ describes web applications with two installable routes: publish a Progressive Web App, or wrap the published app with Capacitor for store submission. A wrapper is not native app generation, and the resulting release still needs platform-specific testing and maintenance. Base44's [store-packaging guide](https://docs.base44.com/documentation/building-your-app/uploading-to-app-stores) requires Builder or higher to download release files. Push notifications are supported with Apple or Firebase credentials; full offline operation and features such as HealthKit are not. Apple and Google accounts and approval remain your responsibility. If a responsive browser experience meets the requirement, test it on physical phones and keep the simpler release path. If push notifications, offline operation, platform payments, or store discovery are mandatory, involve a mobile specialist before choosing either product. ## How Do You Hand a Lovable or Base44 App to a Developer? You can hand a Lovable or Base44 app to a developer by sharing its repository, data model, service accounts, setup instructions, and known limits. Ask the developer to reproduce the main flow before changing it. For Lovable, explain which services and integrations the app uses and whether GitHub is connected. For Base44, identify the managed entities, functions, and integration-credit features. This helps the developer distinguish ordinary frontend code from platform-owned behavior. ## When Do Lovable or Base44 Users Need Help? A non-coder using Lovable or Base44 should get developer help when records disappear, one user can access another user's records, payment states disagree, or a Git conflict is unclear. Preserve the last working version and logs before making more changes. Do not ask the agent to repair production data without a verified backup. Database migrations and identity changes can be irreversible. Rehearse the operation with copied test data and write a rollback plan. When usage charges rise unexpectedly, identify the exact meter before changing plans. Separate build activity, live platform services, external APIs, storage, and traffic. A higher subscription may not fix the service that created the charge. ## The Verdict Choose Lovable for a new web app when visual editing, creating a GitHub repository from the app, and managed backend services suit your team. Choose Base44 when a tightly managed business-app backend and fewer infrastructure choices matter more than being able to move the backend to another provider. Base44 offers web-wrapper store packaging, but neither is the default for fully native screens or an independently hosted backend without migration work. ## Frequently Asked Questions ### Is Lovable cheaper than Base44? Lovable Pro costs more than Base44 Starter but less than Base44 Builder: their monthly prices are $25, $20, and $50 respectively. Builder includes GitHub synchronization and store-file downloads. The plans meter different activities, so a lower subscription does not guarantee a lower running cost. ### Can Lovable import an existing GitHub repository? Lovable can rebuild existing code from a repository ZIP through Upload code when the gradual-rollout import feature is available in your account. It creates a new TanStack Start project; it does not directly connect and continue the original repository. Database records and users require separate migration. ### Can Base44 be self-hosted? Base44's managed backend cannot be moved unchanged to your own server. GitHub tools and the eject command expose code and resource definitions, but ejection creates another Base44 app without copying existing database records. Leaving Base44 requires a separate backend and data migration. ### Can Lovable and Base44 package App Store apps? Base44 provides downloadable iOS and Android store packages on Builder and higher plans, wrapping the published web app in a web view. Lovable documents an external Capacitor wrapper route. Both require developer accounts, device testing, and store review; neither route turns the web interface into native screens. Pricing, code import, security scans, and mobile packaging were checked against official documentation on October 17, 2026. --- ## Lovable vs Bolt.new vs v0: Which Should You Choose? URL: /vibe-coding/comparisons/lovable-vs-bolt-vs-v0/ Cluster: Vibe Coding > Comparisons Lovable, Bolt.new, and v0 can each turn a conversation into a working web application. Each starts with a prompt and a preview. [Differences between vibe coding tools](/vibe-coding/comparisons/) become clearer when you edit the code, publish the app, or bring in another developer. Choose Lovable for a managed web-app flow, Bolt for a visible browser workspace with an Expo path, and v0 for a Next.js project headed to Vercel. Our [vibe coding guide](/vibe-coding/) explains the approach, while the [v0 versus Bolt comparison](/vibe-coding/comparisons/v0-vs-bolt/) explores those two in more detail. ## How Do Lovable, Bolt, and v0 Differ? Lovable provides a managed visual web-app builder, Bolt provides a JavaScript coding workspace in the browser, and v0 focuses on Next.js apps and Vercel hosting. | Criterion | Lovable | Bolt.new | v0 | | Main Surface | Managed builder | Browser workspace | Next.js builder | | App Scope | Web apps | Web and Expo | Full-stack Next.js | | Backend Path | Cloud or Supabase | Bolt Cloud or integrations | Vercel integrations | | Paid Entry | $25 monthly | $25 monthly | $30/user/mo | | Build Meter | Task credits | Tokens | Dollar credits | The prices link to [Lovable's plan documentation](https://docs.lovable.dev/introduction/subscription-plans), [Bolt's pricing page](https://bolt.new/pricing), and [v0's pricing guide](https://v0.app/docs/pricing). The current v0 pricing page calls its $30-per-user offer Plus. Generation credits and deployment charges remain separate, so the prices above are entry subscriptions rather than complete app budgets. ## How Does Editing Differ in Lovable, Bolt, and v0? Lovable emphasizes visual edits and product-level prompts, Bolt exposes files and a terminal alongside chat, and v0 keeps editing close to a Next.js project and Git workflow. Each can revise the first build; the difference is the development context you work in while fixing it. Bolt shows the project files and development tools beside the chat. The product's official introduction describes code view, a browser runtime, package installation, and full-stack JavaScript development. The prompt and the project environment sit together. v0 steers the generated result toward Next.js conventions and Vercel services. Its full-stack documentation covers App Router endpoints and integrations for databases including Supabase, Neon, and Upstash. All three provide visual controls for a targeted change, such as revising a heading or button: | Platform | Visual Editing | How Changes Apply | | Lovable | [Preview toolbar](https://docs.lovable.dev/features/preview-toolbar): inline text, element selection, drawings, comments | Inline text has a limited free allowance; selection and drawing requests use build credits | | Bolt | [Select tool](https://support.bolt.new/building/visual-edits): text and style adjustments | Preview free; saving the batch to code uses tokens | | v0 | [Design Mode](https://v0.app/docs/design-mode): text, typography, color, and layout | Apply sends changes to chat and generates a new version for review | ## Lovable: Managed Product Building Lovable combines chat, visual editing, hosting, version history, and backend services. Its documentation describes Lovable Cloud as the built-in path and Supabase as another backend option. That packaging reduces the number of services a first-time builder must assemble before a database-backed web app can run. Lovable's [official FAQ](https://docs.lovable.dev/introduction/faq) says apps created from May 13, 2026 use TanStack Start. Apps created before that date use React with Vite. Confirm the project's stack before planning a handoff. Lovable synchronizes generated projects through GitHub. Its separate Upload code feature can rebuild a repository ZIP as a new TanStack Start project where the gradual rollout is enabled. It does not directly attach to the original repository; users and database records require separate migration. Lovable generates web applications rather than native iOS or Android projects. Its FAQ documents two routes to an installable result: a Progressive Web App, or a Capacitor wrapper submitted to the App Store or Play Store. Wrapping a web app is different from generating a native app. - Inspect: generated authorization rules and secrets. - Test: GitHub synchronization before parallel edits begin. - Budget: building and deployed Cloud or AI use. - Record: whether the project uses TanStack Start or Vite. ## Bolt: A Browser Development Workspace Bolt provides a JavaScript development environment inside the browser. The official introduction describes websites, full-stack web apps, and mobile apps built with Expo. Code view and a running application stay within the workspace. The visible environment changes the type of instruction available after a failed generation. A developer can identify a file, inspect a dependency, or ask for a targeted repair with the project structure in view. That control carries the normal responsibility to understand the generated change. Bolt measures each generation through a token allowance. Its [token guide](https://support.bolt.new/faqs/account-and-subscription/tokens) explains that input and output both consume the allowance. Project context is a major source of input use, so the same sentence can cost more after the codebase grows. Pro costs $25 per month and includes 10 million monthly tokens. The pricing page says unused paid-plan tokens roll into the next month, with one extra month as the limit. The free plan lists one million monthly tokens and a 300,000-token daily cap. - Inspect: which files enter the model context. - Test: one package change and one failed build. - Budget: repair prompts after the repository grows. - Record: the install and deployment steps outside Bolt. ## v0: Next.js Inside the Vercel Stack v0's [full-stack documentation](https://v0.app/docs/full-stack-apps) covers backend endpoints, environment variables, database connections, and deployment for Next.js applications. The default stack remains specific to the Vercel workflow. The guide says v0 defaults to Next.js for full-stack work and recommends App Router conventions. Vercel deployment and marketplace integrations form the shortest path from generation to a live application. That specificity can reduce translation for a team already using Next.js and Vercel. A team committed to a different framework or hosting model should test generated code in the intended environment before treating the preview as a valid architectural sample. v0 bills model use through dollar-denominated credits. Plus costs $30 per user per month and includes $30 in credits. Monthly credits can roll over, then expire 65 days after issue. Deployment, databases, and other providers can add costs outside the v0 credit balance. A comparison should keep generation spending separate from Vercel usage and any database, email, storage, or model service used by the deployed app. - Inspect: server and client boundaries in generated code. - Test: the application in the target Next.js repository. - Budget: v0 credits and Vercel usage separately. - Record: every marketplace service added to the project. ## Does Lovable, Bolt, or v0 Fit Your Project? Choose Lovable for guided visual web-app work, Bolt for a JavaScript workspace or Expo mobile app, and v0 for an existing Next.js and Vercel workflow. All three can build responsive web apps; repository and mobile requirements narrow the choice. | Constraint | Lovable | Bolt.new | v0 | | Store app | Capacitor wrapper | Expo path | Web app path | | Existing repo | ZIP rebuild, rollout | GitHub import | Git Import | | Default host | Lovable Cloud | Several options | Vercel | | Recovery | Version revert | Version restore | Generation restore | For an existing repository, use the real project structure, dependency policy, and build command in the trial. List required hosts and data providers before generation so a default service does not become an unplanned dependency. Test keyboard navigation, focus order, field labels, error announcements, heading order, and contrast. A screenshot shows layout and color; the other checks require interaction and DOM inspection. The three restore controls have different data and code boundaries: - Lovable: preview an older working state, then revert from History or a past message. The code returns to that state, but database records do not. - Bolt: open Version history, preview a backup, and choose Restore this version . Bolt also leaves the current Bolt or Supabase database unchanged. - v0: choose an earlier generated version and restore it as the new latest version. Direct file edits and other non-message actions may not create a version. ## How Do Lovable, Bolt, and v0 Prices Compare? Lovable Pro and Bolt Pro both start at $25 monthly, while v0 Plus costs $30 per user. Lovable includes 100 monthly credits, Bolt includes ten million monthly tokens, and v0 includes $30 in monthly generation credits. These are different billing units, not comparable quantities of completed work. | Platform | Paid Plan | Included Use | Expiry Rule | | Lovable | Pro, $25/mo | 100 credits | Up to 2 months | | Bolt.new | Pro, $25/mo | 10M tokens | One extra month | | v0 | Plus, $30/user/mo | $30 credits | 65 days | Lovable's credit documentation describes a unified general balance for Build, Cloud, and AI use during its current rollout. Daily Build grants and monthly Cloud or AI grants are consumed before general credits. Monthly subscription credits expire two months after issue, while purchased top-ups last 12 months. The useful comparison unit is a fixed feature. Record the allowance before the build, after generation, after one repair, and after a short deployed test. That log shows how each billing model reacts to the work you actually need. ## Can You Move Lovable, Bolt, and v0 Apps Elsewhere? You can move generated code from Lovable, Bolt, and v0 through GitHub or export workflows. Moving the running app also requires migrating or replacing services such as authentication, databases, storage, and hosting. | Check | Lovable | Bolt.new | v0 | | Code Route | Two-way GitHub | GitHub project | Git repository | | Managed Layer | Cloud services | Hosting options | Vercel services | | Proof Step | Run synced clone | Install from clone | Deploy target repo | Run the handoff while the app is small. Start from a clean clone, use a new environment file, create a test account, write a record, and trigger an error. Document any step that still requires the original builder. Trace the stored data after the code handoff works. Identify the database owner, export format, authentication provider, file storage, and deletion path. A repository can be portable while the application's live records remain expensive to migrate. ## How Can You Compare Lovable, Bolt, and v0 Fairly? Compare Lovable, Bolt, and v0 by giving each the same small task and checking the same result. The brief below is an illustrative exercise, not a report of a test we conducted. > Build a responsive service-request app. Users sign in, create a request, and see only their own records. An admin can update status. Show a clear error when saving fails. Explain the authorization rule and provide setup instructions for a fresh clone. Use the same acceptance checks for every result: - A second user cannot read or modify the first user's request. - A failed database write produces a useful message and preserves form input. - The layout works at a 390-pixel viewport without horizontal scrolling. - A fresh clone starts from written steps and documented environment variables. - The usage log separates generation, repair, deployment, and outside services. A non-developer can count the service choices that block the build. A developer can check code visibility and repair control. A Next.js team can measure how much translation the generated project requires. ## Frequently Asked Questions ### Is Lovable, Bolt, or v0 easiest for a non-developer? Lovable is the clearest starting point for a non-developer who wants a managed visual web-app workflow. Bolt exposes more development tools, while v0 fits a Next.js and Vercel workflow. Lovable still requires the builder to check permissions, stored data, and error handling before launch. ### Does Lovable, Bolt, or v0 give developers more control? Bolt gives developers a visible browser JavaScript workspace with files, commands, and GitHub integration. v0 is the more focused choice for control within a Next.js and Vercel project. Lovable emphasizes managed visual editing instead of direct development work. Code access in any of these tools does not automatically make connected databases and managed services portable. ### Can Lovable, Bolt, and v0 build full-stack apps? Lovable, Bolt, and v0 can all build full-stack web apps. Lovable offers Lovable Cloud or Supabase, Bolt combines JavaScript projects with Bolt Cloud or other services, and v0 supports Next.js server endpoints and database integrations. Generated authorization and error handling still need review. ### Can you export code from Lovable, Bolt, and v0? You can move source code from Lovable, Bolt, and v0 through their GitHub or export workflows. Moving the running app also requires transferring or replacing its database, authentication, files, and hosting. Test those dependencies outside the builder before assuming the app is portable. ## Should You Choose Lovable, Bolt, or v0? Choose Lovable for a managed web-app flow, Bolt for a visible browser development workspace and broader JavaScript or Expo path, or v0 for a Next.js project headed to Vercel. Use the trial above to compare how well each handles your app, how much it costs, and how easily you can continue the work outside the builder. --- ## Replit vs Lovable: Which App Builder Should You Choose? URL: /vibe-coding/comparisons/replit-vs-lovable/ Cluster: Vibe Coding > Comparisons Replit and Lovable both accept plain-language requests, generate working software, and offer a route to deployment. The [choice between app builders](/vibe-coding/comparisons/) starts with how you want to work. Replit gives you a coding workspace with files and commands. Lovable focuses on building and editing a web app through prompts and visual controls. Choose Replit when the project starts from existing code, needs another runtime, or must ship as a native Expo app. Choose Lovable for a new web app built through a guided visual workflow. Our [vibe coding overview](/vibe-coding/) explains where these builders fit. ## Should You Choose Replit or Lovable? Choose Replit for an existing codebase, broader runtime choices, or an Expo mobile app. Choose Lovable for a guided visual web-app workflow with less development tooling to manage. | Requirement | Replit Fit | Lovable Fit | | Existing GitHub project | Direct import path | ZIP rebuild, gradual rollout | | Visual new web app | Supported | Core workflow | | Store app route | Expo native path | Capacitor wrapper | | Runtime choice | Broad workspace control | Platform-guided services | | Minimal tooling exposure | More concepts visible | Better aligned | | Deployment selection | Several explicit types | Direct publishing path | Try a small feature in both tools to see which working style suits you. The [Replit guide](/vibe-coding/platforms/replit/) and [Lovable guide](/vibe-coding/platforms/lovable/) provide additional product detail. ## How Do Replit and Lovable Differ? Replit provides a cloud development workspace with code and runtime controls, while Lovable emphasizes guided web-app generation and visual editing. Both can plan, build, connect data, and publish; their main difference is how much technical control the builder handles directly. The overlap is strongest for a new web application with familiar screens and workflows. A portal, directory, dashboard, booking tool, or simple marketplace can begin in either product. The difference is how quickly the project asks its owner to make technical decisions. Replit keeps files, commands, source control, and deployment types close to the main workflow. Lovable keeps prompting, visual editing, integrations, and managed services close to the main workflow. Neither approach removes the need to test data access, failure handling, and recovery. | Dimension | Replit | Lovable | | Main interface | Cloud development workspace | Guided web-app builder | | Existing repository | GitHub import supported | ZIP rebuild, gradual rollout | | Deployment | Several runtime types | Lovable publishing path | | Store app route | Expo native workflow | Capacitor wrapper | ## What Can You Build with Replit? Replit can build websites, full-stack web apps, scripts, and Expo mobile apps inside a cloud development workspace. Agent works alongside files, a code editor, shell, version control, secrets, previews, and deployment controls. This makes Replit suitable for projects where the implementation itself needs to remain visible and editable. It also means the owner encounters framework, dependency, runtime, and deployment choices. A non-coder should prefer a common stack with clear documentation and local support. Replit's Git tools support GitHub import, branches, repository synchronization, and direct version-control work. Agent checkpoints provide another recovery layer. Use both: checkpoints for quick workspace recovery and meaningful Git commits for review and external ownership. The product can support more than a conventional generated web app because the workspace is not limited to one managed application stack. Confirm any required language, package, background task, or network behavior with a minimal test before the architecture grows. ## What Can You Build with Lovable? Lovable builds full-stack web apps through plain-language prompts and visual editing. A builder can describe pages, select visible elements, connect integrations, and publish. The main workflow asks fewer questions about commands and runtime configuration. Lovable's FAQ describes web applications rather than native app generation. It documents PWA installation and wrapping a published app with Capacitor for store submission. Those routes can suit some projects, but native device behavior and wrapper maintenance remain separate concerns. Lovable can create and synchronize a new GitHub repository. Its separate Upload code feature, currently rolling out by account, rebuilds an existing code ZIP as a new TanStack Start project. It is not a direct continuation of the original repository, and databases and users need separate migration. Code download on paid plans and GitHub access create an inspection and handoff path. The running system may still depend on Lovable Cloud or connected services. Document data, secrets, domains, and provider accounts separately from the repository. ## How Do Replit and Lovable Handle Planning and Editing? Replit supports planning before implementation in its development workspace; Lovable supports planning and visual revision in its web-app workflow. On either platform, keep the first build to one complete task, such as creating and closing a support request. In Replit, inspect the plan, changed files, commands, and checkpoint. In Lovable, inspect the proposed changes, preview, connected services, and Git diff. The interface differs, but both need written acceptance checks that another person can repeat. Ask for one specific change at a time. “Add a required category field and show its validation message” can be reviewed. “Improve the support system” cannot, because it leaves data, workflow, permissions, and success criteria undefined. Replit’s browser-only [Visual Editor](https://docs.replit.com/design/visual-editor) can change text and styling directly without AI credits; complex edits go through Agent and use credits. Lovable’s [Preview toolbar](https://docs.lovable.dev/features/preview-toolbar) combines inline text edits with selected-element requests and drawings. Lovable limits free inline text edits and charges build credits for selection and drawing requests. ## Is Replit or Lovable Cheaper? Replit Core has a lower monthly subscription than Lovable Pro: $20 compared with $25. Replit Core costs $18 per month when billed annually and includes $20 in monthly credits; total app costs depend on usage and hosting. Replit Pro costs $100 monthly or $90 per month billed annually, while Starter is free. Replit's paid Agent modes charge for effort, while Free Mode provides a no-cost allowance. Paid planning and deployment runtime can also use credits. The subscription therefore does not imply unlimited building or hosting. Lovable Free includes five daily build credits with a 30-credit monthly cap. Pro lists 100 credits at $25 monthly or $250 annually. Business lists 100 credits at $50 monthly or $500 annually, with additional organizational controls. Lovable's documentation also lists monthly Cloud and AI grants and higher credit tiers. The current usage guide describes Build, Cloud, and AI consumption. Plan against the balance and usage dashboard shown for the workspace rather than assuming every action uses one identical unit. | Paid Plan | Listed Price | Included Usage | | Replit Core | $20 monthly | $20 monthly credits | | Replit Pro | $100 monthly | $100 monthly credits | | Lovable Pro | $25 monthly | 100 credits | | Lovable Business | $50 monthly | 100 credits plus controls | Set an experiment budget and measure one representative feature. Include planning, generation, one defect repair, deployment, database activity, and external APIs. Review the dashboards after several days before buying annual service or publishing to users. ## Can Replit and Lovable Continue an Existing Codebase? Replit can import an existing GitHub repository directly; Lovable can rebuild a code ZIP as a new project where Upload code is available. Replit is the more direct choice for continuing the original codebase. Check the branch and existing tests before asking Agent to change it. Lovable offers code import through a ZIP rebuild where the feature is enabled. Its GitHub integration then creates a new repository for synchronization. Choose that route when rebuilding is acceptable, rather than assuming it preserves the existing framework and repository history. In either product, the repository is not the whole service. Environment variables, databases, files, DNS, authentication providers, payment products, and deployment settings remain elsewhere. Put those accounts under organizational ownership and document recovery. ## How Do Replit and Lovable Deployment Options Differ? Replit lets you choose Static, Autoscale, Reserved VM, or Scheduled deployments; Lovable offers direct publishing with Lovable Cloud or connected services. Replit hosting is usage-based, so choose its deployment type according to traffic, uptime, and background tasks. Lovable provides a direct publishing path and can use Lovable Cloud services. The convenient route still needs domain ownership, backups, budget controls, monitoring, and rollback. Identify what happens when a credit or external-service limit is reached. Free publishing is not a service-level commitment. Replit's Starter documentation limits the user to one published app and says it can go down after 30 days. Check current plan terms before promising a permanent public URL. For both products, record the deployed revision and verify it after release. Test the main user flow, an error case, and account separation in the deployed environment. A successful build log does not prove production behavior. ## Can Replit and Lovable Build Mobile Apps? Replit generates React Native apps through Expo; Lovable generates web apps that can be packaged using an external wrapper. Their mobile clients let you work on projects from a phone but do not determine the type of app being built. Replit documents an Expo route for native iOS and Android apps. Store submission occurs through the web product on a computer and still requires developer accounts, signed builds, store assets, privacy details, device testing, and platform review. Lovable documents mobile-friendly web applications rather than native generation. Its FAQ describes PWA installation or wrapping the published app with Capacitor for store submission. The Lovable mobile client is a project-building interface, not the packaging step for either route. If a responsive web app is sufficient, test it in phone browsers and keep the simpler delivery model. If push notifications, offline behavior, device APIs, or store distribution are mandatory, write those requirements first and include a mobile specialist. ## What Security Checks Do Replit and Lovable Apps Need? Replit and Lovable apps need checks of dependencies, authentication, server routes, and data access before launch. Lovable offers an automatic Quick security scan on publishing and a deeper code-context scan that can be run manually. A scanner does not establish that either app enforces the intended business rules. Replit projects require the same release discipline even though the controls live in a different workspace. Review dependencies, server routes, secrets, authentication, and deployment configuration. Use separate accounts to test every role and direct data-access path. Develop with synthetic data and separate test credentials. Before launch, document retention, deletion, backups, access, and incident response. Health, financial, identity, children's, or other sensitive data requires qualified security and legal advice. Stop automated fixes when users can see each other's data, records are lost, credentials may be exposed, or payments disagree with the provider. Preserve logs and the last working state. Escalate before another generation overwrites evidence. ## Is Replit or Lovable Better for Learning and Handover? Replit is better for learning code and development tools because it exposes files, commands, Git, and deployment settings. Lovable lets beginners focus longer on visual changes and app behavior. Either can be handed to a developer with its code, service details, and setup instructions. Lovable lets a builder stay focused on product behavior and visual changes for longer. A non-coder still needs to understand users, roles, data, services, and failure paths. Product understanding is not optional simply because implementation is less visible. A developer handoff needs the repository, architecture summary, role matrix, data model, service list, environment-variable names, deployment steps, test accounts, and known limits. The prompt history can supplement that package, but should not be its only explanation. ## How Can You Compare Replit and Lovable Fairly? Test Replit and Lovable with the same small workflow, such as a room-booking form with member and coordinator roles. This is a suggested comparison exercise, not a report of a hands-on test. Build the form, change one requirement, and check that one member cannot view another member’s requests. Record the build cost and the steps needed to deploy and restore a working version. Have a second person run the project from its setup instructions before committing to a platform. Use made-up records that reflect real input, including long names, optional fields left blank, and valid but unexpected values. Check the saved results yourself. An app that works only with the agent’s tidy sample records has not yet passed the exercise. ## The Verdict Choose Replit when the project starts from existing code, requires broader runtime or deployment choices, or needs an Expo path to native mobile output. Choose Lovable when a new web app benefits from a more guided visual workflow and the team is comfortable using Lovable's managed services. Compare the two with the same small project and record the cost and work needed to finish it. ## Frequently Asked Questions ### Is Replit cheaper than Lovable? Replit Core has a lower monthly subscription price than Lovable Pro: $20 versus $25. Core includes $20 in monthly credits, while Lovable Pro includes 100 build credits. These allowances measure different work, and runtime charges can change the total cost. ### Can Lovable import an existing repository? Lovable can rebuild a repository ZIP into a new TanStack Start project when Upload code is available in your account; the feature is rolling out gradually. It does not directly attach to the original GitHub repository, and data migration is separate. Replit remains the more direct route for continuing an existing repository. ### Can Replit build native mobile apps? Replit can build iOS and Android apps using React Native and Expo. Store submission still requires developer accounts, signed builds, device testing, and review. Lovable generates web apps, with an external wrapper such as Capacitor needed for store packaging. ### Is Replit or Lovable better for non-coders? Lovable is the better starting point for a non-coder who wants a guided visual web-app workflow. Replit suits a beginner willing to learn files, commands, and runtime choices, or a project that needs its broader development environment. Both still require testing before customers use the app. Pricing, Lovable code import, and security-scan terminology were checked against official documentation on October 17, 2026. --- ## v0 vs Bolt.new: Which App Builder Fits Your Project? URL: /vibe-coding/comparisons/v0-vs-bolt/ Cluster: Vibe Coding > Comparisons v0 and Bolt can both produce a running web interface from a short request, but they suit different projects. [Compare app-building workflows](/vibe-coding/comparisons/) before choosing. v0 is organized around Next.js, Git, and Vercel. Bolt is organized around a browser JavaScript workspace, Bolt Cloud, and a broader path that includes Expo mobile projects. Choose v0 when the project belongs in a Next.js, Git, and Vercel workflow, and Bolt for a new JavaScript app on Bolt Cloud or an Expo mobile app. For an explanation of the workflow, see [what vibe coding means](/vibe-coding/). For a wider shortlist, see our [vibe coding platform guide](/vibe-coding/platforms/). ## Should You Choose v0 or Bolt? Choose v0 for a Next.js project that fits Vercel and an existing design system. Choose Bolt for a browser JavaScript workspace, integrated Bolt Cloud services, or an Expo mobile app. | Requirement | v0 Fit | Bolt Fit | | Existing Next.js repository | Direct ecosystem fit | Import supported | | Figma-led web work | Documented focused workflow | Integration available | | New JavaScript full stack | Next.js path | Broader guided path | | Native iOS or Android | Not documented | Expo path | | Non-JavaScript backend | External architecture decision | External service required | | Vercel deployment | Core path | Possible external route | Try one small feature in each tool to compare the result and the work needed to fix it. The [v0 guide](/vibe-coding/platforms/v0/) and [Bolt guide](/vibe-coding/platforms/bolt/) provide deeper product coverage. ## How Do v0 and Bolt Differ? v0 centers full-stack web work on Next.js and Vercel, while Bolt combines a JavaScript development workspace with Bolt Cloud and an Expo mobile route. Both can build a database-backed web app; v0 is not limited to interface mockups. Bolt can create websites, JavaScript web applications, and Expo mobile apps in a browser workspace. Bolt Cloud supplies managed hosting, databases, and domains, while integrations connect other services. Bolt's documented backend runtime supports JavaScript rather than PHP or Python. Generated-code maintainability depends on the prompt, repository, selected model, integrations, review, and later edits. A generic quality ranking cannot replace comparison with the project's conventions and tests. | Dimension | v0 | Bolt | | Default web center | Next.js and Vercel | Browser JavaScript workspace | | Existing repositories | Git import and dedicated branch | GitHub import and sync | | Design input | Documented Figma workflow | Figma integration available | | Native mobile | Not the documented center | Expo workflow | ## What Can v0 Build? v0 can create full-stack Next.js applications, use server routes, and connect documented database and AI integrations. The project runs in a Vercel Sandbox during work. A team already using Next.js and Vercel can keep generation close to its existing build and deployment model. Agentic features include web search, browser use, terminal commands, tests, and integrations. v0 exposes permission modes named Ask, Auto, and Full. Permission choice controls how readily the agent acts, but it does not change the need to review the files and commands. v0's Next.js default can be a constraint when the project uses another framework or hosting model. Confirm the actual repository structure and deployment requirement before generation. Rewriting architecture to fit the tool is a larger decision than generating a component. For an established design system, give v0 the relevant components, tokens, and page context rather than a screenshot alone. Require it to reuse existing primitives and accessibility patterns. Review imports and dependencies so a small interface task does not create a competing component library. ## What Can Bolt Build? Bolt can build JavaScript websites, full-stack web apps, and Expo mobile apps. Its workspace combines prompting, files, a terminal, and a live preview. Standard is available on Free and Max on paid plans; Bolt manages the underlying model selection. Bolt can use Bolt Cloud for a database and hosting, or connect documented services. The integrated path reduces setup for a new project. It also means the owner should map which parts live in Bolt, GitHub, a database service, a payment provider, and domain accounts. Bolt runs JavaScript backends in its browser environment. If a project requires Python, PHP, or another backend, that service must live elsewhere. Confirm cross-service authentication, deployment, monitoring, and cost before committing to that split. Bolt's token guidance recommends focused prompts and clear context. State the exact behavior to preserve as well as the requested change. If the agent touches unrelated files, restore a verified version and narrow the request instead of accepting cleanup work that was never required. ## Can v0 and Bolt Use Figma or Existing Code? v0 and Bolt both support Figma input and existing GitHub repositories. v0 requires a paid plan for Figma input and can read a file or selected frame; Bolt imports a selected frame. Specify the screen or flow you want to build. v0 can import public or private Git repositories and monorepos through its GitHub integration. The first code change creates a working branch and preview for review rather than pushing straight to the base branch. Bolt also documents importing an existing GitHub repository, creating repositories, and synchronizing changes. Bolt does not merge branches inside its workspace, so review and conflict resolution happen on GitHub. Run existing tests before and after the first generated change. For either tool, restrict the first task to a low-risk component or page. Exclude authentication, database migrations, build configuration, and payment code. A clean, small diff reveals whether the generated code respects the repository better than a broad application rewrite. Both tools can refine the design visually. In [v0 Design Mode](https://v0.app/docs/design-mode), select an element, adjust its text or styles, then Apply to create a new chat version containing the code changes. Bolt’s [Select tool](https://support.bolt.new/building/visual-edits) collects preview changes before saving them to code; previewing is free, while saving uses tokens. ## How Do v0 and Bolt Planning Controls Differ? Bolt offers Plan Mode before implementation, while v0 exposes Ask, Auto, and Full permission modes for agent actions. Use these controls to review the proposed files and commands before allowing broad changes. Use named checkpoints or commits after a complete behavior passes. Restore rather than asking the agent to repair an uncontrolled rewrite. Meaningful recovery points reduce both risk and avoidable usage. ## Is v0 or Bolt Cheaper? Bolt Pro starts at $25 monthly, compared with $30 per user for v0 Plus. v0 Free gives new users a one-time $5 credit grant and permits seven messages per day; Plus includes $30 in monthly credits and daily login credits. Business costs $100 per user per month and adds organizational privacy controls, including training opt-out. Enterprise uses custom pricing. v0 also lists Mini, Pro, Max, and Max Fast model tiers, each with different token rates. Bolt Free includes one million monthly tokens and a 300,000-token daily limit. Pro starts at $25 per month with ten million monthly tokens and no daily limit. Teams starts at $30 per member per month, while Enterprise uses custom pricing. Bolt says unused paid-plan tokens roll into the next month and remain valid for two months in total. Larger projects can consume more tokens because more files and context must be synchronized. Neither product's allowance maps to a fixed number of completed features. | Plan | Listed Price | Included Meter | | v0 Free | $0 | $5 once; 7 messages daily | | v0 Plus | $30/user/mo | $30 monthly credits | | Bolt Free | $0 | 1M monthly tokens | | Bolt Pro | From $25/mo | 10M monthly tokens | Include external costs in the same worksheet. Database, storage, email, payment, model, domain, and monitoring services can outlive the builder session. Record which subscription or usage meter pays for each item and who receives its alerts. ## How Do v0 and Bolt Backends and Hosting Compare? v0 pairs Next.js server capabilities with database integrations and direct Vercel deployment. Bolt Cloud provides integrated database and hosting services, with external integrations available too. Both approaches can support a full-stack app, but their service accounts and running costs need separate tracking. Bolt Cloud can combine database, hosting, and domain management with the builder. Bolt also supports external integrations. The simpler setup should still be documented as a system map so the team knows which provider holds code, records, files, secrets, and DNS. Test the backend with separate accounts and roles. Confirm password recovery, session expiry, account deletion, direct-record access, and failed external calls. A polished interface cannot prove that the server enforces the intended rules. ## Can You Hand a v0 or Bolt Project to a Developer? You can hand a v0 or Bolt project to a developer through GitHub, with setup instructions and access to its connected services. v0 creates a working branch for code changes, while Bolt leaves branch merging to GitHub. The repository is only the code portion of a handoff. List environment-variable names, service accounts, databases, files, domains, OAuth settings, payment products, email configuration, and deployment permissions. Put them under organization-controlled accounts. Ask a second person to clone the repository on another machine, supply test configuration, and complete the main user flow. If setup depends on undocumented knowledge in the original chat, the handoff has not passed. Keep a concise architecture note beside the code. Explain the runtime, services, data model, roles, deployment path, and known limitations. This protects the project when models, interfaces, and contributors change. ## Can v0 or Bolt Build Native Mobile Apps? Bolt can build native iOS and Android apps through Expo; v0 focuses on Next.js web apps and does not document a native app-building route. Tell Bolt in the first prompt that you want a mobile app so it creates the correct project structure. Store release is a separate stage. It needs a computer, Expo and platform developer accounts, signed builds, device tests, store listings, screenshots, privacy information, and review. Apple or Google may reject a build that worked in preview. v0's documented full-stack path centers Next.js web applications. A responsive v0 site can work well in a phone browser, but that does not make it a native store package. Do not assume store distribution without a documented build and signing workflow. If the requirement is a phone-friendly dashboard, a responsive web app may avoid store overhead. If it includes push notifications, offline behavior, device APIs, platform purchases, or store discovery, Bolt's Expo path is relevant and a mobile specialist should review the release. ## When Do v0 or Bolt Users Need Help? A non-coder should get help when a v0 or Bolt change loses data, exposes records, alters production credentials, or creates an unclear Git conflict. Preserve the last working revision and logs before further edits. Use test data and provider sandboxes during development. For payments, cover success, decline, cancellation, duplicate submission, refund, and webhook retries. Keep live credentials out of prompts and under an organization account. Do not approve a database migration, authentication rewrite, or dependency repair solely from the preview. Ask a qualified developer to inspect the diff, backup, rollback, and deployed behavior. These changes can fail after the visible screen appears correct. ## The Verdict Choose v0 when a web project belongs in a Next.js, Git, and Vercel workflow, especially when work begins from an existing repository or focused Figma input. Choose Bolt when a new JavaScript application benefits from an integrated browser workspace, Bolt Cloud, or an Expo mobile path. Compare both using a small feature from your own project before choosing. ## Frequently Asked Questions ### Is v0 cheaper than Bolt? v0 Plus has a higher monthly entry price than Bolt Pro: $30 per user versus $25. Plus includes $30 in monthly credits, while Bolt Pro includes ten million monthly tokens. Dollar credits and tokens measure different work, so compare the cost of a representative feature rather than only the subscription. ### What are the current v0 plan names? v0's pricing page lists Free, Plus, Business, and Enterprise. Plus costs $30 per user per month and Business costs $100 per user per month, while Enterprise uses custom pricing. v0 also lists Mini, Pro, Max, and Max Fast model tiers with different usage rates. ### Can v0 and Bolt import existing code? v0 and Bolt both support existing GitHub repositories. v0 creates a working branch on the first code change, with a preview for review; Bolt synchronizes repository changes and leaves branch merging to GitHub. Check the existing build before asking either tool to modify it. ### Can v0 or Bolt build native mobile apps? Bolt documents an Expo workflow for native iOS and Android apps, while v0 documents Next.js web apps rather than native app generation. Store release still requires a computer, developer accounts, signed builds, physical-device tests, store assets, privacy information, and platform review. v0's documented full-stack path centers Next.js web applications, so native store delivery should not be assumed from its responsive web output. Pricing, repository import, and platform scope were checked against official documentation on October 17, 2026. --- ## base44 Guide: Features, Pricing, and Limits URL: /vibe-coding/platforms/base44/ Cluster: Vibe Coding > Platforms Base44 is a browser-based app builder that turns a plain-English request into a hosted web application. It supplies the database, user accounts, server functions, and deployment within one managed service. That bundled setup is why the platform attracts beginners and small teams comparing prompt-to-app tools. Choose Base44 for a small app built around stored records when you want fewer services to set up. Before committing to a long-term project, check its two credit balances and how you would move the code and data elsewhere. For the broader workflow and its limits, see our [guide to vibe coding](/vibe-coding/). ## What Is Base44, and Who Is It For? Base44 is an AI app builder for beginners and small teams creating record-based web apps from plain-English descriptions. Its developer documentation lists React and Vite for the screens, a managed record database, user authentication, server functions, and realtime updates. Base44 handles the service setup that often stops a non-technical builder after the first polished screen. The generated app can use data, login flows, email, files, and analytics without requiring a separate account for each service. Common project shapes include dashboards, internal tools, CRMs, schedulers, and small marketplaces. These apps fit the platform's record-based data model and managed services better than a system with unusual infrastructure or a large existing backend. Wix [acquired Base44 on June 18, 2025](https://www.wix.com/press-room/home/post/wix-further-expands-into-vibe-coding-with-acquisition-of-base44-a-hyper-growth-startup-that-simplif) and said it would remain a distinct product. Bringing in a developer later takes some planning. Builder and higher can keep app code in two-way GitHub sync, while the Base44 CLI can copy code and backend resource definitions into a separate project. ## What Does Base44 Include? Base44 includes a managed database, authentication, hosting, file storage, and tools for generating and editing an app. The generation loop works as a conversation. Describe the application, review the result, and use follow-up instructions for behavior changes. The Code tab also exposes generated files for manual edits, a split preview, and direct debugging. Base44's managed services cover data storage, user authentication, email delivery, file storage, analytics, and hosting. The builder can generate a data model, access rules, and UI without asking you to provision each service separately. Review those access rules with separate user accounts before launch. A generated login screen does not prove that every user can read only the records they are authorized to see. Builder and higher support [two-way GitHub sync](https://docs.base44.com/developers/app-code/local-development/github). Changes merged into the repository's `main` branch sync back to Base44, and publishing remains a separate action. Base44 documents several constraints for its GitHub connection. Sync starts on Builder, the supported base branch is named `main`, and Version History states from before the connection cannot be restored through the repository. You can start from a [Base44 or community template](https://docs.base44.com/Getting-Started/App-templates) instead of a blank prompt. Base44 copies the template into your workspace for customization. Templates that use backend functions require Builder or higher, so check the template's requirements before choosing it. Base44's [visual editor](https://docs.base44.com/Building-your-app/Design) lets you select an element and change static text, fonts, spacing, and other styling directly. Dynamic text still needs an AI-chat change. That gives you a more precise route for a small design correction than describing the whole page again. After publishing, [app analytics](https://docs.base44.com/documentation/performance-and-seo/app-analytics) shows visits, unique visitors, visit duration, and traffic sources. Use those measurements to see whether people reach the intended pages. They complement error logs and access tests; visitor counts alone do not show that the app works correctly. ## What Are Base44’s Main Strengths? Base44’s main strength is its bundled app infrastructure, which avoids separate initial setup for authentication, data, email, files, analytics, and hosting. A small internal tool can therefore reach a shareable URL before those services need individual provider accounts and deployment work. A solo founder can put a clickable prototype in front of an early tester without first assembling a database and deployment stack. The result still needs access testing, yet the initial service setup is already present. The Code tab exposes React and Vite files, GitHub sync accepts local edits, and the CLI can create a separate project. Each route still depends on Base44 services unless you replace those services yourself. ## What Are Base44’s Main Limitations? Base44’s main limitations are dependence on its managed backend, separate building and integration credit budgets, and the extra work required to migrate existing data. GitHub sync provides an ongoing code workflow, while eject copies schemas, functions, configuration, and frontend code into a separate Base44 project. The production database stays on the original Base44 app. Moving to another backend provider requires a data export and service migration alongside the code handoff. The credit system has two separate failure points. Free users receive 25 monthly message credits with a five-credit daily cap, and every plan has a separate integration allowance for live app actions. Track both in the workspace usage screen and set a launch budget from expected email, file, image, and AI activity. Base44 documents a managed NoSQL database, entity schemas, and row-level access rules. A brief that needs several tenants, linked record types, or unusual infrastructure should be mapped as entities and permissions before any build. ## How Much Does Base44 Cost? Base44 offers a free plan and paid plans from $20 a month on monthly billing. Its [official pricing page](https://base44.com/pricing) lists the figures below. | Plan | Price | Messages | Integrations | | Free | $0 | 25 | 100 | | Starter | $20 / $16 | 100 | 2,000 | | Builder | $50 / $40 | 250 | 10,000 | | Pro | $100 / $80 | 500 | 20,000 | | Elite | $200 / $160 | 1,200 | 50,000 | Prices are USD per month, shown as monthly billing / annual billing. Messages and Integrations are monthly credit allowances; Free message credits also have a five-credit daily cap. Starter includes unlimited apps and in-app code editing. Builder is the first tier with backend functions, custom domains, GitHub integration, and model selection. Compare this public table with the plan shown in the workspace before subscribing. Message credits pay for AI prompts while you build, while integration credits pay for live actions such as email, file operations, image generation, and LLM calls. Credit use varies by the work performed, and paid balances reset each billing cycle without rolling over. Check both balances before a public launch. Compared with [Lovable](/vibe-coding/platforms/lovable/), Base44 splits building messages from live integration activity. Lovable Pro costs $25 monthly, while Base44 Builder costs $50 monthly or $40 monthly on annual billing. Base44's running backend and data remain managed services even when GitHub sync exposes the app code. ## Base44 vs Lovable Lovable and Base44 both target a builder who wants a managed full-stack start. Lovable can connect an external Supabase project, while Base44 supplies its own managed NoSQL backend. Base44 now provides GitHub sync and an eject workflow, although the original running backend and existing records remain on Base44 infrastructure. In either case, connect the repository, inspect the generated code, and test the data handoff before treating the prototype as a long-term product. See our [Lovable versus Base44 comparison](/vibe-coding/comparisons/lovable-vs-base44/) for the full choice. Both platforms have a free tier, but the paid entry points and billing units differ. Lovable Pro has a published price of $25 monthly. Base44 Starter costs $20 monthly or $16 monthly on annual billing, while Builder costs $50 monthly or $40 monthly on annual billing. ## How Do You Build and Export a Base44 App? Build a Base44 app by describing one workflow and checking the generated result. Export the app code through GitHub synchronization on Builder or higher, or use the CLI eject command to create a separate project. Start with one user role, one core record type, and one action that changes data. A client tracker could begin with contacts and a status update before adding email, files, or AI features. Write the prompt as an acceptance check instead of a product wish list. State who signs in, which records that person may read, and what success looks like after the main action. This gives you a result that can be tested with two separate accounts. - Create one owner account and one restricted test account. - Try direct URLs for records the restricted account should never read. - Inspect failed requests in the Activity Monitor before publishing. - Connect GitHub before broad visual or data-model changes. The code handoff has two different meanings. GitHub sync keeps a Base44 app connected to a repository, while [`base44 eject`](https://docs.base44.com/developers/references/cli/commands/eject) creates an independent local project with a new app ID. The eject documentation says schemas move and existing records stay behind, leaving the new database empty. A handoff plan therefore needs a data export, secret inventory, service map, and a test run outside the original workspace. Some services still run on Base44 when you develop locally. Base44's CLI runs functions, entities, media, and email/password authentication locally, while OAuth and core integrations may still forward to the deployed service. Read those warnings before treating a local preview as an isolated copy. ## How Does Base44 Control Access to App Data? Base44 controls access to app data through rules on entities, the records in its managed NoSQL database. Entity schemas define fields and relationships, while access rules determine which users may read or change each record. Treat those rules as application logic, even when the builder created them from a prompt. A useful review begins with an access matrix. Put each user role in one column and the allowed read, create, update, and delete actions in the others. Translate every row into a test using accounts with different roles and different record ownership. | Role | Record | Allowed action | Denied action | | Member | Own contact | Read and update | Delete another user's | | Manager | Team contact | Read and assign | Change workspace owner | Test the rule through both the interface and a direct record URL. The interface may hide a control correctly while the backend still accepts an unauthorized request. Repeat the test after changing an entity, role, or generated server function. Integration limits can interrupt a published app. An app may continue loading after its email, file, image, or AI allowance is exhausted. Identify the user-facing result for each failed integration and make sure a retry cannot create duplicate records or messages. The Activity Monitor is useful evidence during review because it shows live application requests and errors. Capture the failed authorization and exhausted-credit cases there before publishing, then record which account, action, and entity produced each result. ## How Do You Recover a Broken Base44 App? Recover a broken Base44 app by opening Version History, previewing the last working version, and choosing Revert to this version. You can also use the Revert icon under the prompt that caused the problem. After restoring, test the broken path again. Narrow the next request to one page, entity, or function and state what must stay unchanged. If repeated attempts fail or the issue involves access rules, payments, or data loss, keep the restored version live while a qualified reviewer checks that path. ## Compare Across Platforms The [vibe coding comparisons hub](/vibe-coding/comparisons/) places the major tools beside each other. The [vibe coding platforms overview](/vibe-coding/platforms/) summarizes how each tool works, charges for usage, and lets you export a project. ## Base44 Verdict Base44 is the strongest fit in this group for a beginner building a small record-based prototype with the fewest service choices. Use its managed stack for that advantage, connect GitHub early, and plan a separate data migration if you need to move the app, because eject does not copy existing records. ## Frequently Asked Questions ### What stack does Base44 generate? Base44 documents a React and Vite frontend with Tailwind CSS and shadcn/ui. Its managed backend provides a NoSQL database, built-in authentication, Deno serverless functions, and realtime updates through WebSocket, with the Base44 SDK connecting the generated frontend and backend services. ### How much does Base44 cost? Base44 Free costs $0 while Starter costs $20 monthly or $16 monthly on annual billing. Builder costs $50 monthly or $40 monthly on annual billing, while Pro costs $100 monthly or $80 monthly on annual billing. Elite starts at $200 monthly or $160 monthly on annual billing, with different credit allowances on every tier. ### How do Base44 credits work? Base44 message credits pay for AI building prompts, while integration credits pay for live app actions such as email, file operations, image generation, and LLM calls. Free includes 25 monthly message credits with a five-credit daily cap plus 100 integration credits. Paid credits reset each billing cycle and do not roll over, so monitor both balances before launch. ### Can I export a Base44 app? You can export Base44 app code through GitHub synchronization on Builder and higher plans or through the Base44 CLI eject command. The Base44 CLI eject command creates a separate local project with frontend code, entity schemas, functions, and configuration, but the new Base44 backend starts with an empty database. Plan a separate data migration and test the local project before relying on the handoff. ### Does Base44 include a backend? Base44 includes a managed backend with a NoSQL database, built-in authentication, Deno serverless functions, realtime updates, hosting, and integrations for services such as email and files. The backend is convenient because it needs little setup, but it is still a Base44 service. Review data permissions and document a migration path before storing important production records. Pricing, GitHub sync, and the eject workflow were checked against official documentation on October 17, 2026. --- ## Bolt.new Guide: Features, Pricing, and Limits URL: /vibe-coding/platforms/bolt/ Cluster: Vibe Coding > Platforms Choose Bolt for a JavaScript web or Expo mobile project that benefits from one browser workspace with editable code and GitHub export. Choose another builder if you need a different backend language, and arrange technical help if nobody can maintain the published app. For the wider workflow, see our [introduction to vibe coding](/vibe-coding/). The [Bolt vs Replit comparison](/vibe-coding/comparisons/bolt-vs-replit/) helps when you need a different runtime or cloud workspace. ## What Is Bolt.new? Bolt.new is an AI app builder that runs in your browser and generates applications from natural-language prompts. It combines an AI agent, editable code, a terminal for running commands, and a live preview in one workspace. ## What Can Bolt Build? Bolt builds JavaScript websites, web apps, and mobile apps using Expo. Its workspace can create project files, install JavaScript packages, run development commands, and show the result. The code remains visible, so a user can inspect a file or edit it directly instead of relying only on chat. Bolt's browser environment runs JavaScript backends. Its browser-support documentation rules out running PHP or Python backends there. A prompt can still produce a JavaScript application that calls a Python service hosted elsewhere, but Bolt is not the place where that Python service runs. Bolt Cloud brings managed databases, hosting, and domains into the same product. Integrations add paths to GitHub, Stripe, Expo, and other services. Those connections shorten setup, but each introduces an account, permission set, billing owner, and recovery path that should be recorded before launch. | Project Need | Bolt Path | Check First | | Marketing site | Web project and hosting | Forms, analytics, domain owner | | Account-based app | JavaScript app and database | Roles, recovery, data export | | Native mobile app | Expo project | Store accounts and review | | Python service | External API only | Separate host and deployment | ## What Are Bolt’s Main Limitations? Bolt’s main limitations are its restriction to JavaScript backends, variable token consumption, and the technical judgment needed for complex or sensitive app behavior. Access-control errors, corrupted records, payment disputes, dependency vulnerabilities, and store rejections can have consequences outside the editor. Set a threshold for escalation before users depend on the app. Ask for help when a fix involves database migration, authentication policy, cryptographic keys, webhook signatures, or conflicting Git branches. Preserve a working backup and explain the intended outcome. Repeatedly asking the agent to “try again” can hide the original cause and consume the evidence a developer needs. Use test data that contains no personal or confidential information. Separate development and production credentials. When the app is ready for real accounts, confirm that logs, analytics, error reports, and backups follow the same privacy rules as the visible application. ## How Much Does Bolt Cost? Bolt offers a free plan, Pro from $25 a month, and Teams from $30 per member a month on monthly billing. AI work is measured in tokens. Free includes one million monthly tokens with a 300,000-token daily limit. Pro includes ten million monthly tokens and no daily limit. Enterprise uses custom pricing. The pricing page says paid-plan unused tokens roll into the following month and remain valid for two months in total. Free tokens do not receive the same benefit. Bolt also advertises yearly savings of up to 28 percent, so the exact checkout total depends on plan and billing term. | Plan | Listed Monthly Price | Core Allowance | | Free | $0 | 1M monthly, 300K daily tokens | | Pro | From $25 | 10M monthly tokens | | Teams | From $30 per member | Team controls and tokens | | Enterprise | Custom | Custom limits and support | Token totals do not translate into a fixed number of features. Bolt warns that larger projects require more file syncing, so later prompts can cost more than early prompts. Model selection, context size, repeated debugging, and broad requests also affect consumption. ## How Does Bolt’s Workspace Work? Bolt’s workspace combines a prompt box, editable project files, a terminal, and a live preview. You can ask the agent for a change, inspect its output, and edit the files directly. A beginner does not need to understand every file, but should know which page is being changed, where errors appear, and which action restores a working state. Bolt's agent documentation currently lists Standard and Max. Standard is available on Free, while Max requires a paid plan. Bolt handles model selection for Standard and Max. Forge, where available, adds a separate selection of open-source models and its own usage and data terms. Start by asking for one screen and one complete user action. Confirm that the preview matches the request, then inspect the changed files. Adding authentication, payments, external APIs, and a complex dashboard in the first prompt makes it harder to identify which layer failed. When the preview looks correct, test the behavior that changes data. Refresh the page, sign out and back in, use an invalid value, and repeat the action from another account if roles are involved. A polished screen does not prove that data access or error handling is correct. Bolt also supports [direct visual edits](https://support.bolt.new/building/visual-edits) to text, colors, and formatting through the Select tool. Preview changes collect in a batch before you save them to the code. Trying edits in the preview is free; saving the batch uses tokens. This is useful when you know which element needs changing. You can start from a [Figma frame](https://support.bolt.new/integrations/figma), a single design screen, instead of describing a layout from scratch. Bolt imports one frame rather than a whole Figma project. Add the intended interactions and check the result at different screen sizes; a design reference does not specify every working behavior. Bolt also accepts your own image assets. Its [AI image editing and generation](https://support.bolt.new/building/images) require a paid plan, and generating images uses more tokens than using stock images. This matters when comparing the cost of a visually rich site with a simpler app. ## How Does Bolt Plan Mode Work? Bolt Plan Mode lets the agent examine a request and propose an implementation without making code changes. Bolt's documentation notes one exception: on a new project, the first Plan Mode request creates a basic structure before planning. That distinction matters if the repository must remain untouched during an initial review. A useful plan names the screens, user roles, stored data, external services, and acceptance checks. It should also identify decisions that require a human owner, such as whether deleting an account removes records, who can issue refunds, or what happens when an integration is unavailable. Non-coders should reject plans that hide important behavior behind phrases such as “set up authentication” or “add payments.” Ask which provider is used, where keys live, how failed payments behave, and how an administrator recovers access. The answer becomes a checklist for the build and later handoff. After the plan is approved, request one specific feature or change at a time. Bolt's token guidance recommends focused prompts and clear context. Small changes are also easier to review, restore, and explain to the next person who inherits the project. ## How Can You Control Bolt’s Token Usage? Control Bolt’s token usage with focused requests, a spending limit, and a check of the balance after each change. Record the starting balance, plan the change, build it, fix one realistic error, and record the ending balance. That gives a project-specific reference without pretending that every future change will cost the same. Use Plan Mode for questions about approach, and keep Build Mode for approved edits. Tell the agent which files or behavior must remain unchanged. If a request touches unrelated areas, restore the last working version and narrow the task rather than spending more tokens repairing a broad rewrite. Version history is a cost control as well as a safety feature. Name working checkpoints after completed behavior, such as “account creation and password reset pass.” A meaningful label helps a non-coder choose the correct recovery point without guessing among visually similar snapshots. ## Can You Export Bolt Projects to GitHub? Bolt can connect a project to GitHub, create a repository, import an existing repository, and work with branches. Its documentation says Bolt automatically commits and pulls changes at intervals. The connected repository becomes the durable record that another developer can inspect without entering the original Bolt workspace. Bolt does not merge branches inside the product. That work happens on GitHub, where conflicts and review comments must be resolved. A non-coder should ask a developer to review any conflict involving authentication, database schemas, payments, or access rules rather than choosing whichever block looks newer. A project download is another exit path, but code is only part of a running service. Environment variables, databases, uploaded files, DNS records, payment products, email templates, and OAuth settings may remain in external systems. List each system, its owner, and its export or recovery procedure. Prove portability before launch. Download or clone the repository onto a separate machine, follow written setup instructions, and run the main user flow. If that cannot be done without the original builder explaining undocumented steps, the handoff is incomplete. ## Can Bolt Build Mobile Apps? Bolt can build iOS and Android apps through Expo. A responsive website is a separate output and does not become an App Store app just because it fits a phone screen. The first prompt must identify the project as a mobile app so the correct structure is created. Bolt's Expo guide separates building from store submission. Submission requires a computer, Node.js, Git, an Expo account, platform developer accounts, signed builds, store metadata, screenshots, privacy disclosures, and review. Apple and Google can reject a build even when it works in a local preview. For a non-coder, the safer route is to validate the main flow with Expo Go, then involve someone familiar with EAS builds and store requirements. Budget for developer-account fees, device testing, privacy documents, and review time. Do not promise a release date based only on the generated preview. If the requirement is simply a phone-friendly booking form or dashboard, a responsive web app may avoid store overhead. If push notifications, device APIs, offline behavior, or store distribution are essential, write those requirements before generation and test them on physical devices. ## Does Bolt Include a Database and Hosting? Bolt Cloud can supply a database and hosting, while Bolt also documents external integrations. Define what data is collected, who can read it, how it is backed up, and how it is deleted before inviting users. Authentication needs more than a sign-in screen. Test password reset, duplicate accounts, expired sessions, deleted users, and every role. For a client portal, create two test customers and confirm that neither can reach the other's records by changing a URL or request identifier. Payment work needs a provider test environment and official test payment methods. Confirm success, failure, cancellation, refund, duplicate submission, and webhook retry behavior. Keep product IDs, prices, tax settings, and refund permissions under an account owned by the business, not by a temporary contractor. Hosting also needs a rollback and domain plan. Record where DNS is managed, which account pays for hosting, and how a prior working deployment can be restored. A shareable preview URL is useful for review, but it is not an operations plan. ## How Does Collaboration Work in Bolt? Bolt's collaboration documentation says collaborators share one project chat and only one prompt can be processed at a time. The project owner retains control over GitHub and certain integrations. That makes ownership more important than simply inviting everyone to the workspace. Assign one person to accept changes and one person to test them. A reviewer should compare the requested behavior with the changed files, then run the affected flow. Visual approval alone misses broken validation, inaccessible controls, and data rules that only fail under a second account. Keep decisions outside the chat as well. A short repository document can record the user roles, external services, environment variables, deployment route, and known limits. Chat history is useful context, but it is a poor substitute for a handoff that can be checked line by line. ## A Safe First Project A reading list is a useful first exercise because it has visible state changes without payments or sensitive data. Ask for a responsive page where a user can add a title, mark it as read, filter the list, and remove an entry. Keep accounts and cloud storage out of the first pass. 1. Use Plan Mode to list the four actions, the page structure, and the acceptance checks. 2. Build the static interface and verify keyboard access at phone and desktop widths. 3. Add local persistence, then refresh and confirm that entries remain. 4. Enter empty, very long, and repeated titles to test validation. 5. Create a named working checkpoint before requesting visual changes. 6. Connect GitHub, clone the repository elsewhere, and follow the setup instructions. Success means every action works, errors are understandable, and a second person can run the project from the repository. The [platform guide](/vibe-coding/platforms/) provides alternatives if Bolt's runtime, budget, or ownership model does not match the next project. If you need help beyond a generated fix, Bolt provides community support through Discord for all plans and email support for paid plans. Its [help center](https://support.bolt.new/) links to preview, database, publishing, and account troubleshooting. Choose the route that matches the failure before spending more tokens on the same unsuccessful prompt. ## The Verdict Bolt is the better fit when a JavaScript web or Expo mobile project needs an integrated browser workspace, visible code, and a practical GitHub exit. Choose another route when the backend must use a different language, nobody can own production operations, or the project cannot absorb token variability and the specialist review required for sensitive features. ## Frequently Asked Questions ### What can Bolt build? Bolt builds JavaScript-based websites, web apps, and Expo mobile apps in a browser workspace. Bolt Cloud can supply hosting, databases, domains, and other managed services, while integrations cover services such as GitHub and Stripe. A project that needs a non-JavaScript backend is outside Bolt's documented runtime support. ### How much does Bolt cost? Bolt's pricing page lists Free at $0 with one million monthly tokens and a 300,000-token daily limit. Pro starts at $25 per month with ten million monthly tokens and no daily limit. Teams starts at $30 per member per month, while Enterprise uses custom pricing. ### Can I move a Bolt project elsewhere? You can move a Bolt project’s source code through GitHub integration or a project-file download. Moving the files does not automatically move domains, secrets, databases, user records, or third-party accounts, so test the exported project outside Bolt and write down every external dependency before treating the handoff as complete. ### Can Bolt build an App Store app? Bolt can create native iOS and Android projects with Expo, but generation is only the first stage. Store submission requires a computer, local tooling, an Expo account, Apple or Google developer accounts, signed builds, store listings, privacy information, testing, and review by the relevant store. Pricing, supported technologies, agents, GitHub, and mobile publishing checked against official documentation on October 17, 2026. --- ## Lovable: The Complete Guide to Building Apps URL: /vibe-coding/platforms/lovable/ Cluster: Vibe Coding > Platforms Lovable is a browser-based [vibe coding platform](/vibe-coding/platforms/) that generates a web app's screens in React and TypeScript through chat and a live preview. Lovable Cloud or an external Supabase project can supply the database, user accounts, and server code before the app is published to a shareable URL. Choose it when a founder or operator needs a managed full-stack prototype that a developer will review before launch. Check the running costs and export options before committing to a larger app. ## What Is Lovable? Lovable generates React and TypeScript web applications from conversational prompts. Projects can use Lovable Cloud or an external Supabase project for authentication, data, server functions, and hosting. You describe the result, review the generated application, and refine it through chat or visual edits. The target audience is founders who want to test an idea before hiring a developer and operators who need an internal tool before engineering has room for it. Lovable gives that group a short route from a product description to a shareable application, with less infrastructure setup than a code-first workflow. Lovable's [ownership terms](https://lovable.dev/pricing) say you own the generated code, apps, websites, customer data, and AI output, subject to third-party model rights. Lovable generates web applications rather than native iOS or Android projects. Its FAQ documents two packaging routes: publish an installable Progressive Web App, or wrap the published web app with Capacitor for App Store or Play Store submission. ## Who Should Use Lovable? Lovable suits non-technical founders, product teams testing ideas, and teams prototyping internal tools. It also suits a team that wants to build the first version itself and give a developer the remaining work. The wrong fit is an app with sensitive data, payment logic, or complex permissions that nobody can review before launch. That review is required for the risk, regardless of which generator wrote the code. A prototype can prove the workflow and expose missing requirements before a larger build begins. A maintained product still needs a named owner for security, data, deployment, and the generated code. ## What Can Lovable Build? Lovable can build web-app interfaces, dashboards, internal tools, and prototypes with login and stored data. Its managed services and integrations can add payments and other backend functions, but the generated behavior still needs checking. | Feature | What it gives you | Check before launch | | Auth and data | Cloud or Supabase | Two-account access test | | Payments | Stripe or Paddle | Provider go-live checklist | | Visual editing | Selected preview element | Generated diff | | GitHub | Two-way code sync | Clean-clone run | Lovable can also add AI features to the app your users open, such as a voice tutor, document assistant, or research panel with cited web answers. These are separate from the agent that builds your app. Lovable's [supported AI models and tools](https://lovable.dev/blog/ai-models-and-tools-in-lovable) include services for conversation, research, media generation, and task routing. Choose those services for a specific user task, then test their answers and running costs. An AI feature inside a published app continues to use resources when visitors use it, even after you finish building. ## What Are Lovable’s Main Strengths? Lovable’s main strengths are its short setup path, visual iteration, and managed backend because the first build can use its preview, cloud services, and publishing controls in one workspace. Short setup path: a non-technical founder can move from a description to a working prototype without first configuring a local toolchain. That gives a potential user or teammate something concrete to test before custom infrastructure enters the plan. Supabase scaffolding: Lovable can generate authentication, database schemas, row-level security policies, and real-time subscriptions for an external Supabase project. Treat the generated schema and policies as a reviewable starting point, then test every role against the real domain rules. Iterative UI refinement: Selecting an element in the Preview toolbar lets a builder identify the exact part of the app before asking for a text, spacing, color, image, or layout change. The generated diff shows whether that focused request also changed shared code. Non-technical collaboration: a founder or product manager can drive product decisions without involving an engineer for every copy or layout change, leaving engineering time for work that needs deeper technical judgment. ## What Are Lovable’s Limitations? Lovable’s main limitations are the review needed for complex business logic, dependence on managed services, and usage costs during repeated AI changes. Security still belongs to the app owner: Lovable runs database, RLS, and dependency checks before publishing, with an on-demand code security scan in the Security view. Its [security documentation](https://docs.lovable.dev/features/security) requires a thorough review beyond those tools. Resolve every critical finding, run the code scan manually, and test authorization with separate accounts before real data or payments go live. Business rules need proof: multi-step rules, permission models, and database queries need direct tests. Exported code needs maintenance: the files can move to GitHub, yet a developer must still inspect component size, repeated patterns, tests, and dependency choices before accepting long-term maintenance. Credit costs follow task complexity: the $25 monthly Pro plan shares a workspace balance across building, hosting, and in-app AI. Read the message-level usage record and set per-member limits before a team starts iterating. Some integrations need code: Lovable documents built-in payment and Supabase paths, while custom APIs, Slack webhooks, Twilio, and other services may require backend functions or direct code work. Check the current connectors and payment docs before promising an integration. ## How Much Does Lovable Cost? Lovable offers a free plan; paid plans start at $25 a month for Pro and $50 a month for Business on monthly billing. Credits pay for building, Lovable Cloud, and AI features inside published apps. [Lovable's pricing page](https://lovable.dev/pricing) lists the plans below. | Plan | Price | Credits and notes | | Free | $0 | 30 build; 20 Cloud; 4 AI | | Pro | $25/mo, or $21/mo annual | 100 monthly; top-ups; domains | | Business | $50/mo, or $42/mo annual | 100 monthly; SSO; private sites | | Enterprise | Custom | SCIM; audit logs; controls | Free provides five daily build credits up to a monthly cap of 30. Free and Pro can publish publicly or restrict access to invited people. Business and Enterprise add workspace-wide and custom audience controls. Project access and published website access are separate settings. Build Mode costs vary with the work performed. Plan Mode costs one credit per message plus any subagent research. Check each message’s recorded cost instead of estimating from prompt count. Monthly plan credits expire two months after issue, annual plan credits expire one month after the annual period ends, and top-up credits last twelve months. Daily build grants expire at the end of each day. ## How Does Lovable Work? Lovable works through a chat window alongside a live preview of your app. You describe the result, and Lovable generates or updates the application while keeping its files available through the code editor or GitHub. The [Preview toolbar](https://docs.lovable.dev/features/preview-toolbar) lets you select elements, edit text inline, draw annotations, and leave comments on the app. The [getting-started guide](https://docs.lovable.dev/introduction/getting-started) also documents Agent mode, Plan mode, version history, GitHub, and publishing. Selecting a preview element gives Lovable a specific target without requiring a non-coder to name its file or component. Plan Mode investigates and plans without modifying code. A message costs one credit plus any subagent research; Build Mode charges according to the work performed. Changes can also sync to a GitHub repository, which matters when a developer will eventually take over the code. If a change breaks a working flow, preview the last good state in Version History and use Revert. Lovable also lets you edit a past message and revert from that point. The chat keeps later work available to reapply, while the restore covers code and leaves database records unchanged. Test the restored flow before prompting again. Narrow the next request to one page or behavior and list what must stay unchanged. Stop after two focused repair attempts, or immediately for another user's data, payment state, or exposed secrets, and keep the last good version while a qualified reviewer checks the risky path. Selection and drawing requests use build credits. Inline text edits have a free allowance, with account and workspace limits; additional edits use credits. Comments are free to leave, but sending a comment thread to Lovable uses credits. A small visual change is not automatically a free change. ## Can Lovable Build Mobile Apps? Lovable builds mobile-friendly web apps rather than native mobile projects. A mobile layout can run in a phone browser, but that does not create native Swift or Kotlin screens or native device behavior. The FAQ names a Progressive Web App as the first installable route. A PWA can be added to a phone's home screen from the published site. It remains a web app, so browser rules and PWA feature limits still apply. For App Store or Play Store submission, Lovable points to a wrapper such as Capacitor. The wrapper places the published web application inside a native shell. Store accounts, signing, icons, permission declarations, device testing, and store review remain separate work. Decide which result the project needs before building. If a home-screen icon and responsive browser experience are enough, test the PWA route on the target phones. If the brief requires store distribution or native device APIs, write those requirements down and test the Capacitor package before the web app gains important data or payment flows. A non-coder can prepare that test without writing native code: publish a throwaway copy, install it as a PWA, and list every phone feature the app needs. Stop if the wrapper requires permissions, background work, purchases, or data handling that nobody on the project can review. Keep the normal web version available while a mobile reviewer checks the store package. ## How Can You Reduce Wasted Lovable Credits? Reduce wasted Lovable credits by describing one clear outcome, the expected behavior, and what must stay unchanged before asking for an edit. Check the recorded usage after each request so repeated repairs do not consume the budget unnoticed. "Add a subscription page with Free, Pro, and Business tiers, a comparison table, and a checkout button for each paid tier. Keep the existing navigation and authentication flow unchanged." This tells Lovable exactly what to add and what to leave alone. A vague prompt leaves design, behavior, and service choices for Lovable to infer, which can require additional paid corrections. Replace "make it look better" or "add payments" with named states, provider choices, acceptance checks, and a list of code that must stay unchanged. Describe the user action and expected outcome in the same sentence, then state what must stay untouched. Use Plan Mode before a feature with several moving parts. Its clarifying questions usually expose decisions that should be settled before code generation. ## The Go-Live Review Lovable's preview is a working application, yet a public launch adds data, identity, payment, and billing risks. Review those areas in a fixed order before sharing the production URL. Start the launch review in the Security view. Lovable's [security documentation](https://docs.lovable.dev/features/security) says automatic checks cover database access, dependencies, code, and common configuration risks. Lovable separates Quick and Deep security scans. Quick runs automatically before publishing and excludes application-code analysis; Deep includes application code, runs on demand, and can be scheduled on Enterprise. Critical findings do not block publishing by default, although workspace controls can enforce a block. Resolve the findings and test access with separate accounts. Check project access and website access separately. The [publishing guide](https://docs.lovable.dev/features/publish) now lists invite-only publishing for Free and Pro, as well as public access. Business and Enterprise support workspace and custom audiences. Set the intended audience before publishing. - Run the on-demand code security review. - Test every user role with separate accounts. - Confirm secrets stay in server-side storage. - Complete the payment provider's live checklist. - Push the final reviewed code to GitHub. Built-in payments require Lovable Cloud. Lovable's [payments guide](https://docs.lovable.dev/features/payments) also explains that Stripe and Paddle use separate test and live environments. ## Can You Export Lovable Code to GitHub? You can export Lovable code to GitHub and synchronize changes in both directions with the connected repository’s default branch, as explained in Lovable’s [GitHub documentation](https://docs.lovable.dev/integrations/github). Changes made in Lovable reach GitHub, while changes merged to that branch return to Lovable. Connect the repository before a large build when the plan permits it. Make one small change in Lovable, inspect its commit, then merge one outside change back through the connected branch. Record the successful direction, branch name, and commit SHA for both checks. Starting from existing code uses a different route: [Upload code](https://docs.lovable.dev/features/projects/import-project) rebuilds a repository ZIP as a new TanStack Start project. The feature is rolling out by account. It does not attach to the old repository, and database records and users require separate migration. The repository becomes the code source of truth after connection. Lovable warns that renaming, moving, or deleting it can break the link. Put repository ownership and transfer rules in the handoff notes before a contractor or agency receives access. A developer taking over needs more than source files. List the active Lovable Cloud or Supabase project, environment variables, authentication providers, custom domain, storage buckets, server functions, payment accounts, and publishing settings. Mark which items have an export path and which require manual recreation. Run the repository outside Lovable with a clean checkout and documented setup command. Replace production secrets with test values, load sample records, and execute the main user flow. The exercise exposes undeclared platform services while the original project is still available for comparison. Continue reviewing synchronized changes as normal pull requests. A visual edit can touch shared components or generated styles beyond the selected element, and a prompt can modify backend code along with the screen. The diff remains the reliable record of what changed. Select Enterprise customers can use an [AWS deployment option in private preview](https://lovable.dev/en/blog/deploy-on-aws). The app, database, storage, and integration gateway run in the customer's AWS account. Lovable still hosts the editor, agent, and workspace controls and processes AI requests made while building. This is not a fully self-hosted Lovable installation. The AWS preview does not yet match every feature of standard Lovable hosting. Confirm availability and the required features with Lovable before planning a migration. ## How Does Lovable Compare with Other Builders? Lovable emphasizes a managed app-building workflow, while Bolt exposes more development tools, v0 centers React and Next.js work, and Replit provides a fuller cloud coding environment. Compare those differences in the [vibe coding comparisons hub](/vibe-coding/comparisons/). Bolt keeps the code and terminal central, which suits developers who want direct control during the browser build. v0 builds full-stack Next.js apps, connects to databases such as Supabase and Neon, and deploys directly to Vercel. Replit is a full browser-based coding environment with an AI agent on top, which makes it deeper but more demanding. Lovable remains the more managed option for someone who does not want to work in the code. ## Lovable Verdict Lovable is the best starting point in this group for a non-technical founder who needs a managed full-stack prototype and expects a developer to review it before launch. The credit system, publishing access settings, and security ownership make it a poor unattended route for sensitive data or payments. ## Frequently Asked Questions ### Is Lovable safe? Lovable provides security scanners, but those tools do not guarantee that a generated app is safe. Its documentation requires a review beyond the automated checks. Before launch, clear every critical finding, run the on-demand code scan, test each user's data access, keep secrets in server-side storage, and get an independent review for sensitive data, payments, or critical business actions. ### How much does Lovable cost? Lovable has a free plan with five daily build credits, capped at 30 a month. Pro costs $25 monthly or $21 monthly on annual billing and starts with 100 monthly credits, while Business starts at $50 monthly or $42 monthly on annual billing. Building, Cloud hosting, and in-app AI can draw from one credit balance. ### Do I own the code I build with Lovable? You own the code, apps, websites, customer data, and AI output you create with Lovable, subject to third-party rights in the underlying models. Keep a GitHub copy and test the project outside Lovable before a serious launch, because code ownership does not automatically migrate hosted data, secrets, or every managed cloud service. ### Does Lovable use Supabase? Lovable supports external Supabase projects and can generate authentication, PostgreSQL data access, Edge Functions, and row-level security policies around them. Lovable Cloud is now another backend path, and some newer features such as built-in payments require Lovable Cloud rather than an external Supabase connection. Check which backend a project uses before planning security, billing, or handoff. ### How does Lovable sync with GitHub? Lovable creates a repository and uses two-way sync with its default branch. Changes from Lovable appear in GitHub, while changes merged to the connected branch return to Lovable; the repository becomes the code source of truth, so renaming, moving, or deleting it can break the connection. ## Where to Go Next The [vibe coding overview](/vibe-coding/) covers the category's broader tradeoffs. If you have grown past a browser-based builder and want to work inside a normal codebase, the [AI coding tools](/ai-coding-tools/) section is the natural next stop. Pricing, publishing access, and security guidance checked against official documentation on October 17, 2026. --- ## Replit Guide: Features, Pricing, and Limits URL: /vibe-coding/platforms/replit/ Cluster: Vibe Coding > Platforms Replit combines an AI agent with a full development workspace that runs in the browser. The editor exposes files, a terminal for running commands, previews, a saved Git history of code changes, and publishing controls in the same project. Those visible development tools distinguish Replit from a builder that keeps most code work behind a simpler prompt interface. Choose Replit when you want Agent help while reading and editing the code yourself, and set a usage limit first, because paid Agent work and hosting can draw from the same credit allowance. Our [vibe coding guide](/vibe-coding/) explains how this prompt-led workflow differs from reviewing and writing every change yourself. ## What Is Replit? Replit is a cloud development environment that runs in your browser. A project includes a file explorer, editor, command terminal, preview, and Git-based version control without a local installation. Version control records file changes so they can be reviewed or restored. That low-setup workflow suits a learner who wants to start coding before dealing with language runtimes or build tools on a personal computer. Replit presents the runtime and project files inside a general cloud IDE. Before choosing it for a non-React project, create the intended template, run its build command, and confirm that Agent can explain the resulting files and errors. Replit's multiplayer environment lets several people work in one project and see edits in real time. That fits pair programming, teaching, and collaborative review inside the full IDE. Core supports up to five collaborators, while Pro supports up to 15, according to the [official pricing page](https://replit.com/pricing). Deployment is built into the platform, with publishing workflows for static, autoscale, reserved VM, and scheduled projects. A project can receive a live URL without configuring a separate hosting provider or writing a new deployment pipeline. The deployment type, secrets, usage charges, and production logs still need review before sharing it. ## What Can Replit Agent Do? Replit Agent can generate an app, explain code, plan changes, and edit project files inside the browser workspace. You can ask for an explanation, request a targeted Lite build, plan a change, or let a paid-plan Full build work through a larger task. Beginners should still read the generated code and error output. Agent can shorten the debugging loop, but it does not remove the need to test the result. Agent can scaffold a project from a plain-English description, including the file structure, routes, and initial logic. Replit's [Starter documentation](https://docs.replit.com/billing/plans/starter-plan) separates Lite build from the Full build and Plan Mode available on Core and Pro. Practical starting projects include a landing page, inventory tracker, customer dashboard, or mobile prototype. Replit exposes the code and runtime alongside the result, so you can keep developing beyond the first generated screen. Agent works beside the actual code from the first moment. When a generated function is wrong, you can edit the file directly, inspect the terminal, or restore a checkpoint before asking Agent to continue. Replit's [iOS and Android app](https://replit.com/products/mobile) can build and publish websites from a phone. Replit directs native iOS and Android work to its web editor, Expo previews, and the normal app-store review process. ## Who Should Choose Replit? Replit suits learners and developers who want AI assistance alongside editable files, a terminal, and shared development tools. It occupies the cloud-IDE part of the [vibe coding platforms](/vibe-coding/platforms/) category. Lovable and Base44 put more of the initial service setup behind managed builder screens, while Replit exposes the editor, terminal, Git state, and runtime throughout the build. A dedicated prompt-to-app platform asks for less direct code involvement when the main goal is a quick deployed result. Factor in the mobile app and multiplayer features if either fits your workflow. Replit's documented iOS and Android builder, Expo workflow, and real-time editing support mobile development and collaboration, though other platforms also support mobile building. ## What Are Replit’s Main Strengths? Replit’s main strengths are its browser-based coding workspace, visible project files, and real-time collaboration. The workspace avoids a local runtime installation for the first session. A learner can open the generated files, run a command, read an error, and compare a checkpoint without first resolving a local language version or missing build tool. The full IDE keeps the terminal, file explorer, preview, and Git history beside Agent. Those tools let a learner follow a generated change and let a developer fix the code directly when Agent gets it wrong. The documented iOS and Android builder is a practical difference for anyone who wants to start an Agent task or inspect a small change away from a laptop. Multiplayer collaboration also sets Replit apart. Several AI-first platforms now support shared projects, but Replit's real-time editing inside a full coding environment remains a more natural fit for pair programming sessions and technical teaching. ## What Are Replit’s Main Limitations? Replit’s main limitations are the attention its generated code still needs and the variable cost of paid Agent work and cloud hosting. Lovable can produce a more guided first full-stack result, while Replit rewards someone willing to inspect files, direct Agent, and edit the output. The credit system combines a subscription allowance with paid Agent charges and cloud usage; Free Mode does not use the paid Agent allowance. Total cost depends on the work Agent performs and the services a deployed project consumes. Set usage limits before a Full build and review the itemized record after each small task. ## How Much Does Replit Cost? Replit offers a free Starter plan, Core at $20 a month, and Pro at $100 a month on monthly billing. Paid plans combine a subscription with credits for paid Agent work and cloud services. The [official pricing page](https://replit.com/pricing) lists the plans below. | Plan | Price | Included usage | Main fit | | Starter | $0 | Lite build; one 30-day app | Learning | | Core | $20/mo, or $18/mo annual | $20 credits; 5 collaborators | Personal apps | | Pro | $100/mo, or $90/mo annual | $100 credits; 15 collaborators | Team builds | | Enterprise | Custom | Custom seats; SSO; controls | Governed teams | Core and Pro include Full build, Plan Mode, and published apps. The [AI billing guide](https://docs.replit.com/billing/ai-billing) distinguishes no-cost Free Mode from paid work, which uses effort-based pricing. Replit asks for confirmation before a paid action starts. Replit exposes usage alerts, service budgets, and spending limits. Set the limit before starting Full build or publishing an app, then check whether it stops the relevant service or only sends a notification. The expected behavior should be part of the project's operating notes. Separate build cost from runtime cost in any estimate. Agent charges belong to creating and correcting the project, while publishing, databases, storage, and network transfer belong to running it. A small build test cannot predict the hosting bill for a busy public app. ## How Does Replit Host Your App? Replit manages the infrastructure behind its publishing products and assigns a live URL to a deployment. That removes a separate hosting account from the first release, while the project owner still chooses the deployment type and monitors its usage. Publishing is usage-based after the included credits. Replit's [publishing guide](https://docs.replit.com/learn/projects-and-artifacts/replit-deployments) lists Autoscale, Reserved VM, Static, and Scheduled deployment types. Static sites, request-driven web apps, continuously running services, and scheduled jobs need different deployment behavior. Choose among Static, Autoscale, Reserved VM, and Scheduled publishing from the application's runtime pattern rather than accepting one default for every project. ## How Do You Start a Replit Agent Project? Start a Replit Agent project by describing one small user task, checking the generated files and preview, and testing the result before adding features. Ask for a small inventory app with an item list and an add form before adding accounts, payments, imports, or background jobs. This makes the first credit cost easier to attribute. Use Plan Mode on Core or Pro to settle the stack and file changes before a larger build. Paid-mode planning can incur effort-based charges even without a code change. Free Mode provides no-cost Agent work within its allowance. For example, you could ask: `Plan a search filter for the existing products page. List the files you expect to change, explain the data flow, and wait for approval before editing.` After approval, ask Agent to build the smallest slice and run the project's existing tests. Open the changed files in the editor before accepting another request. A passing preview can still hide a broken permission check or a failing path that the first demo never reached. - Set a usage limit before the first Full build. - Keep each request tied to one user-visible result. - Read the checkpoint diff before the next prompt. - Publish only after testing a fresh deployment snapshot. Agent checkpoints preserve project files, conversation context, and connected database state at useful milestones. Git commits still make a better long-term record for work that will move between tools or involve outside contributors. If a change breaks the app, open History in the Agent panel, inspect the checkpoint's Changes view, and choose Rollback here. Replit shows what will be restored, including a database option. Test the restored flow before making another request. When the result is close, narrow the prompt to the one broken behavior and keep the parts that work. Roll back when important behavior broke or Agent changed more than expected. If repeated fixes fail, or a change exposes private data or affects payments, stop prompting and keep the safe checkpoint while a qualified reviewer checks the failure. You can also begin with existing code. Replit's [Git tools](https://docs.replit.com/replit-workspace/workspace-features/version-control) support importing a GitHub repository, reviewing changes, and pushing updates back. Bring the code first, then check its environment variables, database connections, and build command before asking Agent to extend it. ## Can You Move a Replit Project to Another Host? You can move Replit project code to another host through Git, but databases, secrets, and hosted services need a separate migration. The Git pane can create branches, show changes, handle merges, and connect to GitHub, while the terminal exposes standard Git commands for developers who prefer them. A code push covers only the repository. Before changing hosts, list the project's secrets, database contents, object storage, domain settings, scheduled tasks, and deployment commands. Each item needs an equivalent service or an explicit decision to remove it. Export the database through its documented path instead of copying files from a running deployment. Replit's published filesystem is based on a deployment snapshot and should never hold records that must survive a restart. Run the project in the destination before moving traffic. Confirm the build command, runtime version, health route, environment variables, and database connection with test data. Keep the Replit deployment available until the new host passes the same user flow. ## Compare the Platforms The [vibe coding comparisons](/vibe-coding/comparisons/) hub places Replit beside the prompt-to-app builders on billing, code access, backend services, and handoff work. For a closer choice, compare [Replit with Lovable](/vibe-coding/comparisons/replit-vs-lovable/) or [Bolt with Replit](/vibe-coding/comparisons/bolt-vs-replit/). ## Replit Verdict Replit is the strongest platform here for a beginner developer who wants Agent assistance while learning from visible code, a terminal, and Git history. The shared credit pool makes budget controls mandatory, while non-coders seeking the shortest managed path from prompt to app will find Lovable or Base44 easier. ## Frequently Asked Questions ### What does Replit Starter include? Replit's Starter plan includes Lite build, limited Agent chat, Design Canvas, Visual Editor, and one published app whose free link expires after 30 days. Full build, Plan Mode, third-party connectors, and more published apps require Core or Pro. ### How much does Replit cost? Replit Starter has no monthly subscription fee. Core costs $20 monthly or $18 monthly on annual billing and includes $20 in monthly credits, while Pro costs $100 monthly or $90 monthly on annual billing and includes $100 in monthly credits. Enterprise uses custom pricing, while taxes and usage beyond included credits can raise the bill. ### What do Replit credits pay for? Replit Core and Pro credits cover paid Agent work and cloud services such as publishing, databases, storage, and network transfer. Paid Agent work uses effort-based pricing; Free Mode provides no-cost work within its allowance. Replit provides usage tracking, budget settings, and usage limits; set those before an autonomous build or public launch rather than checking after the credit balance disappears. ### Can I build a mobile app with Replit? Replit can build mobile apps through a workflow that starts with Agent, previews in iOS Simulator or Android Emulator, and tests on a phone through Expo Go. Its mobile app for iOS and Android also lets you chat with Agent, edit a project, and deploy from a touch interface. App Store release still needs the separate platform process. ### Can I move a Replit project elsewhere? You can move a Replit project’s code to another repository or host using its project files and Git workflows. The code is only part of the handoff. Document secrets, database contents, object storage, deployment settings, and any Replit integrations first. Run the project outside Replit before treating the migration as complete. Plan prices, Starter allowances, and Agent billing were checked against official sources on October 17, 2026. --- ## v0 by Vercel: Features, Pricing, and Limits URL: /vibe-coding/platforms/v0/ Cluster: Vibe Coding > Platforms v0 is Vercel's browser-based platform for generating screens and full-stack web apps, meaning the interface and server code, from prompts, screenshots, Figma files, or existing repositories. It now includes an AI agent, an isolated preview, saved Git code changes, database connections, and direct Vercel publishing. Choose v0 for a React or Next.js project that already uses GitHub and will deploy on Vercel, and expect more setup and less benefit outside that stack. Generation credits and hosting costs are separate considerations once you have a working app. See our [introduction to vibe coding](/vibe-coding/) for the broader approach behind prompt-based app building. ## What Is v0, and Who Is It For? v0 is Vercel’s AI app builder for React developers and technically literate designers who want to turn prompts or designs into editable web applications. It creates a working branch, commits changes, and opens a pull request while leaving the selected base branch protected. The underlying stack favors Next.js and React with TypeScript, Tailwind CSS, shadcn/ui, and Radix UI primitives. Because Vercel builds v0 and runs the surrounding Next.js deployment platform, one click can take a generated project to a Vercel preview URL. v0 fits when you can review a codebase and want AI to accelerate frontend and Next.js work. Its interface also serves founders and designers, but the generated project remains code that someone should inspect. The fit becomes weaker when nobody owns the stack, access rules, environment variables, or production deployment after generation. ## What Can v0 Do? v0 can turn a text brief, screenshot, or Figma frame into a working interface. Vercel's [Figma guide](https://v0.app/docs/figma) supports file or frame links on paid plans and reads layout, tokens, text, and assets for the requested flow. The [current model lineup](https://v0.app/pricing) is v0 Mini, v0 Pro, v0 Max, and v0 Max Fast. The live pricing page also publishes separate input, cache, and output token rates for each tier. The full editor and agent can work across files, run shell commands, inspect a repository, execute tests, and call Vercel or GitHub tools. Vercel documents those actions as [agent capabilities](https://v0.app/docs/agentic-features) in the product. That is closer to [Bolt](/vibe-coding/platforms/bolt/) or [Replit](/vibe-coding/platforms/replit/), although v0 remains more tightly integrated with Vercel for Next.js projects. Git integration supports a familiar sequence: generate a component or feature, push to a working branch, review the GitHub pull request, and merge after the repository's checks pass. Teams already using GitHub and Vercel can review changes and preview the app through the tools they already use. ## What Are v0’s Main Strengths? v0’s main strengths are its React and Next.js focus, design inputs, and integrated GitHub-to-Vercel workflow. Its documentation emphasizes expertise on Next.js, React, Tailwind CSS, shadcn/ui, and the AI SDK. That stack focus gives the agent a clearer default than a platform trying to treat every framework the same. The Vercel integration gives Next.js projects a short path from generated code to a preview URL. Other platforms can deploy to Vercel, but v0 shares projects, environment variables, integrations, domains, and deployment state with the Vercel account. The v0 model tiers let you trade speed and cost against reasoning depth. Mini is the low-cost option, Pro is the balanced default tier, and Max or Max Fast cost more for difficult work. Read the live token rates before choosing one for a long generation. Git Import puts changes to an existing project on a branch you can review, rather than making you copy and paste them. That workflow is easier to audit because the pull request shows exactly which files the agent changed. ## What Are v0’s Main Limitations? v0’s main limitations are its strongest fit with the Vercel stack, usage-based generation costs, and the need for someone to review the resulting code. Git Import lets v0 work with an existing repository, and GitHub becomes the source of truth. The simplest preview and production path still assumes a connected Vercel project. v0 can generate Next.js server actions and API routes, then connect to Supabase, Neon, Upstash, and Vercel Blob. Complex services outside that model still need separate architecture and deployment work, so check which backend services you would need to host separately before choosing v0 for the whole application. The credit model gives you token rates for each v0 model, while the final cost still depends on input context and generated output. Check the usage screen after a small task before estimating a long session. v0 expects someone to review code, branches, environment variables, and deployments. A team without that owner should compare the more managed workflows in the [vibe coding comparisons](/vibe-coding/comparisons/) before committing production data. ## How Much Does v0 Cost? v0 has a free plan, Plus at $30 per user a month, and Business at $100 per user a month. Generation uses credits; Vercel hosting and connected services can have separate charges. | Plan | Price | Credits and limits | | Free | $0 | $5 one-time for new users; 7 daily messages | | Plus | $30/user/mo | $30 monthly; $2 daily login | | Business | $100/user/mo | $30 monthly; training opt-out | | Enterprise | Custom quote | SAML SSO; RBAC; SLAs | Plus and Business accounts can purchase more credits. Plus also includes shared chats and central billing, while Business adds training opt-out by default. The dollar-denominated balance still maps to model-specific input, cache, and output rates. A longer repository context or a higher-cost model can consume more of that balance, so check the usage record after the same small task used in any platform comparison. ## How Do You Start a Project in v0? Start a v0 project with a text prompt, a screenshot or Figma design, or an existing GitHub repository. A blank prompt works for a new page or a small app with a settled goal. Name the users, main screen, data source, and one action that must work. Ask for the interface first when the data model still needs discussion. A design input works when visual intent already exists. Attach one Figma frame or screenshot, then check spacing, responsive behavior, keyboard focus, and component states before asking v0 to add backend logic. Git Import is the path for an existing codebase. The [Git Import guide](https://v0.app/docs/git-import) supports public repositories, private repositories, and monorepos that the installed Vercel GitHub App can access. v0 creates a dedicated working branch from the chosen base. Generated changes are committed to the working branch and previewed through Vercel. Review that branch through a pull request before merging into the base branch. | Starting point | Useful input | First check | | Blank prompt | Users and one task | Scope and data flow | | Figma or image | One clear frame | Responsive states | | GitHub import | Repo and base branch | Diff and tests | ## How Do You Turn a Design Into Usable Code with v0? Turn a design into usable code with v0 by supplying a focused screenshot or Figma frame, specifying interactive states, and reviewing the generated components against your project’s conventions. Before generation, list the interactive states the image cannot show: loading, empty, error, validation, keyboard focus, long content, and narrow screens. For a large Figma file, name the page or flow you want v0 to build. Start with one component or page region, compare it against the source at desktop and mobile widths, and correct the design tokens before composing the full route. Give repeated colors, spacing, and typography a named home in the generated project. A request such as `Use the existing design tokens and components; report any missing token before adding a new value` limits one-off styling and produces a smaller diff. When v0 opens a pull request, review the semantic structure before pixel detail. Check heading order, form labels, button names, focus behavior, image alternatives, and the DOM order used on small screens. A close screenshot match can still leave keyboard and screen-reader users with a broken flow. Next, check how the generated code fits the rest of the project. Confirm that the generated component uses the repository's existing primitives, data-fetching pattern, route structure, and test setup. Reject a parallel component library or duplicate helper unless the change explains why the existing code cannot serve the requirement. The final check is responsive behavior with real content. Use long names, missing images, validation errors, and an empty result set. Design input gives v0 a useful starting shape; repository conventions and state coverage determine whether the result belongs in the product. For repeated work, [Design Systems 2.0](https://v0.app/docs/design-systems-2) can save your team's components, shared style values, and conventions for later chats. Supply the actual component source and an app that uses it, then review the starter before saving it. This gives future screens a consistent foundation instead of another description of your brand. ## Can v0 Build the Backend? v0 can generate a Next.js backend and connect database and storage services. Vercel's [full-stack guide](https://v0.app/docs/full-stack-apps) documents Next.js server actions and API routes as v0's primary backend path. It also lists Supabase, Neon, Upstash, and Vercel Blob among the database and storage connections. Each connection introduces credentials and provider-specific access rules. Store secrets through the environment-variable interface, use server-only variables for privileged keys, and verify which values are available in preview and production. A successful preview can still use a different database or missing secret after deployment. Database work needs a schema review and two-account authorization test. Inspect migrations, uniqueness rules, foreign keys, indexes, and row-level policies. Then create records as one user and attempt to read or change them as another user through both the interface and a direct request. ## How Do You Recover a Broken v0 Project? Recover a broken v0 project by restoring the last working generation from the version menu, then checking the restored preview and any external data separately. v0 creates a new latest version from the restored code, so the failed attempt stays in the chat history. Narrow the next prompt to one failed behavior and name what must not change. If repeated repairs fail or a change affects private data, payments, or secrets, pause further generation. Keep the working version deployed while a qualified reviewer checks that path. Restoring generated code does not prove that an external database was rolled back. A Vercel preview proves that the project builds and serves a page. Data rules, error paths, and payment safety need separate checks while the work still sits on the v0 branch. Start with the generated diff, the list of files and lines changed on the v0 branch. Open it in v0 or the GitHub pull request, then look for new dependencies, public environment variables, broad database policies, and server actions that trust browser values. Any secret prefixed with `NEXT_PUBLIC_` becomes available to client code. Keep privileged service keys in server-only variables and check the preview and production settings separately. Run the repository's existing checks from the sandbox or continuous integration. A useful request is: `Run the unit tests, report every failure, and do not change code until you explain the likely cause.` That keeps diagnosis separate from a broad repair pass. ## How Does v0 Compare With Other Builders? v0 occupies the React and Vercel-focused part of the [platform category](/vibe-coding/platforms/). Managed app builders put more backend setup behind their own services, while editor tools such as Cursor or [Copilot](/ai-coding-tools/copilot/) begin inside an existing local repository. The closest comparison is with Lovable and [Bolt](/vibe-coding/platforms/bolt/). All three can produce a running application from a prompt. v0 concentrates on React and Next.js output plus the Vercel workflow. Lovable provides a more managed backend experience for non-developers, while Bolt keeps a browser terminal and broader scaffolding workflow central. Replit covers more languages through a full cloud IDE. For the closest matchups, see [v0 versus Bolt](/vibe-coding/comparisons/v0-vs-bolt/) and [Lovable, Bolt, and v0 compared](/vibe-coding/comparisons/lovable-vs-bolt-vs-v0/). For a broader comparison, the [vibe coding comparisons hub](/vibe-coding/comparisons/) breaks down the full category. ## v0 Verdict v0 is the best match among these browser builders for a React or Next.js project that already uses GitHub and will deploy on Vercel. Its branch-based import path makes generated work inspectable, while teams outside that stack should expect more setup and less benefit from the platform's tight Vercel connection. ## Frequently Asked Questions ### Which frameworks does v0 handle best? Vercel's documentation says v0 supports popular frontend frameworks and UI libraries, with its strongest expertise in Next.js, React, Tailwind CSS, shadcn/ui, and the AI SDK. Other frameworks can work, while the official full-stack guide calls Next.js the most reliable route for server actions, API routes, and Vercel deployment. ### How much does v0 cost? v0 Free includes $5 in one-time credits for new users and seven messages a day. Plus costs $30 per user monthly with $30 monthly credits and $2 in daily login credits. Business costs $100 per user monthly with the same included credits plus training opt-out, while Enterprise uses custom pricing. ### What models does v0 use? v0's pricing page lists v0 Mini, v0 Pro, v0 Max, and v0 Max Fast model tiers. Vercel AI Gateway is a separate integration that v0 can add to an application you are building, giving that app access to external models. ### Can v0 build a full-stack app? v0 can build full-stack Next.js apps with server actions, API routes, database and storage integrations, and Vercel deployment. Its documented integrations include Supabase, Neon, Upstash, and Vercel Blob. Next.js is its most reliable full-stack path. Complex services outside that model still need deliberate architecture, security testing, and cost controls. ### Can v0 work with an existing GitHub repository? v0 Git Import can open a public, private, or monorepo project that your Vercel GitHub App can access. v0 creates a working branch, commits generated changes, and opens a pull request rather than modifying the base branch directly. Once connected, GitHub is the source of truth, so protect the repository and review every generated change. Pricing and Git Import behavior were checked against official sources on October 17, 2026. --- # AI Agents and Automations ## AI Agents and Automations: What They Are and How They Differ URL: /ai-agents-and-automations/ Cluster: AI Agents and Automations > Overview & AI Agents and Automations Ask five vendors what an AI agent is and you'll get five answers. The word now covers a help-desk chatbot, a Zapier Zap and an app that books your flights, and those three behave nothing alike. I've built and shipped agents, from voice agents on real phone lines to coding agents in my own repos, and one question cuts through the labels: what does the product do once you hand it a job? Ask that, and every tool lands in one of five groups: - [Business and enterprise agents](#what-are-business-and-enterprise-ai-agents) that a company switches on for its staff. - [Voice agents](#what-are-ai-voice-agents) that answer and make calls. - [Workflow automation platforms](#what-are-workflow-automation-platforms) such as Zapier, Make and n8n. - [Agent frameworks and platforms](#what-are-ai-agent-frameworks-and-platforms) that developers build on. - [Personal agents](#what-are-personal-ai-agents) that work for you alone. ## What Is an AI Agent? An AI agent is software that uses a language model to work toward a goal you give it. It decides its own next step, uses tools to act on things like files, inboxes and web pages, and keeps going until the job is done or it needs your input. An AI agent combines three core components: - A goal that you set, such as "find a free slot next week and book the dentist". - A language model, the AI behind apps like ChatGPT and Claude, that looks at where things stand and picks the next action. - Tools the model can call to act in the world, from a web search to your calendar. It also needs a stopping point: it finishes, or it hands the problem back to you. Asked to book the dentist, a chatbot tells you how. An agent checks your calendar, opens the booking page, picks a slot and asks you before it confirms. Product pages rarely mention the stopping point, so it's the first thing I ask a vendor about. ### A Quick Test You Can Use Give the software an instruction and watch what it does next: - Does it keep choosing what to do next without asking you at every step? - Does it act (send, book, edit, buy), or does it only answer? - Does it stop by itself when the job is done? Three yeses and it's behaving as an agent. Test the feature you'd actually use, because one app can be a chatbot in one mode and an agent in another. ### Where Did AI Agents Come From? AI agents came out of AI research, long before the products: Stuart Russell and Peter Norvig's AI textbook defined an agent in 1995 as anything that perceives its environment through sensors and acts on it through effectors. - 1995: textbook agents, described by how a program decides what to do. - 2010s: rule-based automation platforms and robotic process automation spread through offices. - Spring 2023: AutoGPT and BabyAGI showed a language model looping on its own goals, shortly after GPT-4 launched. - 2025 and 2026: agents became products you subscribe to, switch on at work or run yourself. Tutorials from the 2023 wave often name tools that have since been retired or renamed, so check the [retired and renamed table](#which-agent-products-have-been-retired-or-renamed) before you follow one. ## Agent, Chatbot or Automation? The difference is who picks the next step: you as you go (chatbot), you in advance (automation), or the model while it works (agent). Judge the mode you're using rather than the brand: plain ChatGPT chat is an assistant, while ChatGPT Work, which takes a whole outcome and delivers it, is an agent. | Type | Who Picks the Next Step | Example | | Chatbot or assistant | You, one request at a time | Plain ChatGPT or Claude chat | | AI agent | The model, while it works | ChatGPT Work finishing a report | | Automation | You, when you designed it | A Zapier Zap | | Agentic automation | You, with one model step inside | A Make scenario with an AI agent | | Coding agent | The model, checked by your tests | Claude Code or Codex | A chat that runs one web search is still a chatbot. It used a tool once and then waits for you. An automation follows the path you drew in advance, and when it breaks it usually stops with an error. An agent can go wrong quietly and still sound sure of itself, and the [safety section](#are-ai-agents-safe-to-use) covers how to catch that. Vendors use "assistant", "copilot" and "agent" loosely, so this cluster files every product by who picks the next step. ### What Does "Agentic" Mean? "Agentic" describes behaviour: a system is agentic when it pursues a goal over several steps and picks its own next move. "Agentic AI" is the industry label for the whole trend, and an "agentic workflow" is a fixed process with a model-driven step inside it, the agentic automation row above. The word now turns up on spreadsheets and support desks, so check the docs, where a feature has to spell out what it does. ### Which One Suits the Job? - The same steps every time, like copying form entries into a sheet: an automation. - Mostly fixed with one judgement call, like reading a messy email and routing it to the right team: an automation with an AI step. - The path depends on what it finds, like researching three suppliers and drafting a comparison: an agent. - The result is a change to code: a coding agent. An automation is cheaper to run and easier to audit, since it follows the rules you wrote, while every model step an agent takes uses tokens (the units AI usage is billed in) and time. I'd put most people on the second option first: you get the model's judgement exactly where you need it, and every run still leaves a log you can read line by line. ### Where Do Coding Agents Fit? Coding agents live in our [AI coding tools hub](/ai-coding-tools/), outside this cluster: any tool whose main job is changing code in a repository goes there. They can check their work against your tests and builds, a safety net most general agents lack. A failing build is the cheapest way to catch an agent that got it wrong. The comparison of [terminal and IDE coding agents](/ai-coding-tools/comparisons/terminal-vs-ide-agents/) covers when that matters, and our guides to [Claude Code](/ai-coding-tools/claude-code/) and [Codex](/ai-coding-tools/codex/) go deeper on the terminal side. ## The Five Kinds of AI Agents and Automation Tools We sort everything in this cluster into five kinds of product. | Category | What It Covers | Start Here If… | | [Business and Enterprise AI Agents](#what-are-business-and-enterprise-ai-agents) | Agents a company switches on for staff or customers | Your company already pays for a big work suite | | [AI Voice Agents](#what-are-ai-voice-agents) | Agents you talk to by phone or in an app | You want calls answered or made | | [Workflow Automation Platforms](#what-are-workflow-automation-platforms) | Fixed workflows between apps, with optional AI steps | The job repeats the same way each time | | [AI Agent Frameworks and Platforms](#what-are-ai-agent-frameworks-and-platforms) | Code and services developers build agents with | You're building your own agent | | [Personal AI Agents](#what-are-personal-ai-agents) | Agents that act for one person in their own accounts | You want help with your own inbox and errands | If it's a kit for building your own agent, it goes in frameworks. If you sign up and hand it tasks, it's a business agent when a company turns it on for staff and a personal agent when it works for you alone. Every product table below also has a Hosting column that says who runs the product: Self-hosted, Hosted (cloud) or Both. ### Why ChatGPT, Claude and Gemini Appear Twice They appear twice because we file by plan, and each of these brands sells plans in more than one category. Their developer kits sit in a third: | Category | Plans or Kits | | Business agents | ChatGPT and Claude work plans, Gemini Enterprise | | Personal agents | Individual ChatGPT and Claude plans, Gemini Spark | | Frameworks | Kits such as the OpenAI Agents SDK and Claude Agent SDK | ## What Are Business and Enterprise AI Agents? Business and enterprise AI agents are agents a company switches on for its staff or its customers. They take assigned work inside company tools, run on schedules or triggers, and log what they did so an admin can check. They're built for the managers and admins who switch them on, and nearly all of them run in the vendor's cloud. | Product | What It Is | Hosting | | Microsoft 365 Copilot and Copilot Studio | Agent features in Office apps, plus Microsoft's agent builder | Hosted (cloud) | | Salesforce Agentforce and Slackbot | Salesforce's agents, with Slackbot as the agent built into Slack | Hosted (cloud) | | Gemini Enterprise | Google's app for finding, building and running company agents | Hosted (cloud) | | ChatGPT and Claude work plans | Outcome tasks and team agents, with Claude Tag for Slack | Hosted (cloud) | | Notion Custom Agents | Agents that run on a schedule or trigger inside Notion | Hosted (cloud) | The Business and Enterprise AI Agents page also covers Atlassian Rovo, HubSpot Breeze, Grok Bot for Enterprise and Perplexity Enterprise. If your company already pays for one of these suites, I'd pilot that vendor's agents with one team and one named outcome before anyone buys more seats. ### When Does a Copilot Become an Agent? A copilot becomes an agent when it starts work without being prompted each time. Notion AI began as a prompted assistant, and Custom Agents arrived in Notion 3.3 on February 24, 2026, running on a schedule or a trigger with nobody watching. Anything that runs unwatched needs a named owner to read what it did overnight. ## What Are AI Voice Agents? An AI voice agent is an agent you talk to, on a phone line or in an app. It listens, decides what to do mid-conversation, speaks the reply and can take actions such as rebooking an appointment. Speech is why voice gets its own category, and its own AI Voice Agents page: the agent has to answer within a second or so and cope with being interrupted. | Product | What It Is | Hosting | | ElevenAgents | ElevenLabs' platform for voice and chat agents | Hosted (cloud) | | Vapi | Developer platform for building voice agents | Hosted (cloud) | | Retell AI | Phone agents with a no-code flow builder | Hosted (cloud) | | LiveKit | Open-source agent framework, plus LiveKit Cloud to run it | Both | | OpenAI Realtime API | A speech-to-speech model API for voice agents | Hosted (cloud) | Under most phone agents sits a phone-network layer, such as Twilio or Telnyx, that connects the agent to real numbers. Test any of these on a real call, because a demo call can hide slow replies. ### Do Callers Have to Be Told It's AI? Yes, in many places, and the rules differ by country: - In the US, the FCC ruled in February 2024 that AI-generated voices count as "artificial" under the Telephone Consumer Protection Act, so those calls need the same prior consent as robocalls. - In the EU, the AI Act has required telling people they're talking to an AI system since August 2, 2026, unless that's already obvious. ## What Are Workflow Automation Platforms? A workflow automation platform connects your apps and runs fixed "when this happens, do that" workflows between them, mostly without code. Most now let you drop an AI agent step into a workflow, which makes them the easiest place to try a little AI without handing over the whole job. | Product | What It Is | Hosting | | Zapier | No-code Zaps between apps, with AI by Zapier steps | Hosted (cloud) | | Make | Visual scenario builder with Make AI Agents | Hosted (cloud) | | n8n | Open-source workflow builder with an AI Agent node | Both | | Power Automate | Microsoft's automation tool, which can call Copilot Studio agents | Hosted (cloud) | | UiPath | RPA platform with agents on top through Agent Builder | Both | n8n's licence limits commercial use: you can run it for your own business, but you can't resell it as a service. The Workflow Automation Platforms page compares all five with Activepieces, the open-source alternative people name next to n8n. ### Is RPA the Same Thing? Not exactly: robotic process automation (RPA) overlaps with workflow automation, but it scripts the clicks and keystrokes a person would make on screen. That helps with old systems that have no API, the connection point software uses to talk to other software. ## What Are AI Agent Frameworks and Platforms? AI agent frameworks and platforms are what developers use to build and run their own agents: code libraries, vendor SDKs and hosted services. You don't need any of them to use the agents in the other four categories. | Product | What It Is | Hosting | | Vercel AI SDK | Open-source TypeScript toolkit for AI apps and agents | Self-hosted | | LangChain and LangGraph | Open-source agent frameworks, with LangGraph as the runtime | Both | | OpenAI and Claude agent SDKs | Each lab's kit for building agents in your own code | Self-hosted | | Cloudflare Agents SDK | Open-source SDK for stateful agents that run on Cloudflare | Hosted (cloud) | | Dify | Visual builder for agent apps, open source or as Dify Cloud | Both | You'll also meet CrewAI, Amazon Bedrock AgentCore, Microsoft Agent Framework and Google's Agent Development Kit, all covered on the AI Agent Frameworks and Platforms page. Agents built with any of these usually reach their tools through MCP, which our [MCP and hooks guide](/ai-coding-tools/claude-code/mcp-and-hooks/) explains from the Claude Code side. If you're new to this layer, write the plain agent loop once by hand before picking a framework, so you know what the framework is doing for you. ### Framework or Platform: What's the Difference? A framework is code you run yourself: you install a library, write the agent and host it where you like. A platform is a service that runs the agent for you, such as Claude Managed Agents, the OpenAI Agents API or Bedrock AgentCore. Most of the big vendors now sell both. Dify Cloud is a hosted, visual option aimed at non-coders. ## What Are Personal AI Agents? A personal AI agent acts for one person, in that person's own email, calendar, files and accounts, and reports back through an app or a chat. Most are hosted apps you sign up for, and a few are open-source agents you run on your own machine. | Product | What It Is | Hosting | | Muse | Meta's agent for bookings, forms and shopping | Hosted (cloud) | | Manus | Turns one prompt into a finished document, sheet or site | Hosted (cloud) | | Gemini Spark | Google's experimental always-on agent | Hosted (cloud) | | OpenClaw | Open-source agent you run with a model you choose | Self-hosted | | Hermes Agent | Open-source agent from Nous Research with memory | Both | The Personal AI Agents page also covers the individual ChatGPT and Claude plans, Grok Bot, Perplexity Computer and OpenWorker, Andrew Ng's open-source desktop agent. Muse checks with you before sensitive actions like a purchase, which is the stop to look for first. Pilot anything this new on a task you can undo before you link it to accounts you can't afford to lose. ## What About Browser and Computer-Use Agents? Browser and computer-use agents, which click through web pages or a desktop on your behalf, were headline products in 2025 and are now a feature inside other products. Anthropic and OpenAI sell computer use to developers as a tool, open-source projects like Browser Use and Skyvern do the same, and the personal agents above use it to book and buy. Browsing on its own says little about how capable an agent is, so we judge them by the tasks they finish. ## Are AI Agents Safe to Use? They're usable once you put guard rails around them. The biggest risk is prompt injection, where instructions hidden in an email, document or web page get followed as if you'd typed them yourself. The second is reach: an agent that runs on your own computer can read, change and delete whatever your account can. ### How Do You Keep an AI Agent Safe? Make it ask before anything irreversible, and give it as little access as the job allows: - Turn on approval before irreversible actions like payments, sends and deletes. - Give the agent the least access that still gets the job done, and a separate account or mailbox where you can. - Keep tasks short. By our own arithmetic, if each of 10 steps works 95% of the time, all 10 succeed together only about 60% of the time. - Read the logs for the first few runs of anything new. Our research for this cluster showed the same split in all five categories: agents do well at reading and searching, and go wrong when they write and act. OWASP now publishes a Top 10 for Agentic Applications, and the [Hades worm](/news/security/hades-worm-ai-developers/) showed attackers planting instructions for AI coding tools inside ordinary repositories. Our [application security hub](/appsec/) covers the scanning side, and the [Claude Code workflows and safety guide](/ai-coding-tools/claude-code/workflows-and-safety/) shows how approval settings work in one real agent. ## Which Agent Products Have Been Retired or Renamed? These agent products have been retired, renamed or folded into others since March 2025. The When column gives the date each change took effect, or when it started for changes still rolling out. | Old Name | What Happened | When | | ChatGPT agent | Retired; OpenAI points to ChatGPT Work | 2026 | | OpenAI Swarm | Replaced by the OpenAI Agents SDK | March 2025 | | OpenAI Agent Builder | Shuts down; use the Agents SDK or workspace agents | November 30, 2026 | | AutoGen and Semantic Kernel | Succeeded by Microsoft Agent Framework, still supported | April 2026 | | Google Agentspace | Folded into Gemini Enterprise | October 2025 | | Google Workspace Flows | Became Workspace Studio | December 2025 | | Vertex AI | Now part of Gemini Enterprise Agent Platform | April 2026 | | Bedrock Agents | Now "Classic", closed to new customers; AgentCore replaces it | July 30, 2026 | | Zapier Agents | Moving into the AI by Zapier step | From July 2026 | | ElevenLabs Conversational AI | Renamed; now ElevenAgents | From September 2025 | | Intercom | Now Fin, owned by Salesforce | September 10, 2026 | | Claude Cowork | Merging into ordinary Claude | From September 16, 2026 | | Relay.app | Shut down and deleted users' workflows | September 2026 | | Flowise | GitHub repository archived | August 2026 | ## What Else Do People Ask? ### What are the five types of AI agents I keep seeing? The five commonly cited types of AI agents are simple reflex, model-based, goal-based, utility-based and learning agents. Russell and Norvig's AI textbook describes the first four as basic agent programs and shows how any of them can become a learning agent. Those agent types describe how a program makes decisions; product categories describe the jobs agents help people do. ### What is a multi-agent system? A multi-agent system is a group of AI agents that work towards a shared goal, with different roles and handoffs between them, like the crews you build in CrewAI. A multi-agent setup can require more model calls and coordination, increasing costs and making debugging harder than with one agent that has a clear job. ### How is agentic AI different from generative AI? Generative AI produces content when asked, such as text, images or code. Agentic AI uses that ability to pursue a goal over several steps and take actions with tools, and most agents are generative models given tools and a goal. ### What makes an AI agent expensive to run? An AI agent's running costs depend on model usage, task length and retries. Each model call processes tokens, so longer tasks and repeated attempts can increase usage charges. Some products also charge per seat, per task or per resolved conversation. For high-volume jobs that run the same way every time, a fixed automation is almost always cheaper. ### Do I need to code to use an AI agent? You do not need to code to use an AI agent through a no-code product. Business agents, hosted personal agents, voice agent builders and workflow automation platforms offer interfaces for configuring agents without writing code. The frameworks are for building your own agent, mostly with code, and self-hosted agents like OpenClaw need some comfort with installing software. ### What is MCP, and why do agent tools keep mentioning it? The Model Context Protocol (MCP) is an open-source standard for connecting AI applications to outside systems such as files, databases and tools, and its own docs compare it to a USB-C port. An agent that supports MCP can use any tool that offers an MCP server, which is why so many agent products list it. ### Are AI agents the same as Siri or Alexa? Siri and Alexa aren't agents by default, because they're voice assistants that mostly handle one request, like setting a timer, and then wait. A voice assistant behaves as an agent only when it takes a goal and chooses several steps on its own. ## Where Should You Start? Start with the job, then pick the category: - You run a team on Microsoft 365, Salesforce or Google Workspace: that vendor's agents, on the Business and Enterprise AI Agents page. - You want your evenings back from repetitive admin: the Workflow Automation Platforms page, with one AI step. - Your business lives on the phone: the AI Voice Agents page. - You build software: the AI Agent Frameworks and Platforms page, or our AI coding tools hub if the agent should change code. - You want help with your own inbox and calendar: the Personal AI Agents page, starting with a hosted agent. If I could send most readers to one page first, it'd be workflow automation: it's the cheapest way to learn where a model's judgement helps and where a plain rule does better, and you can read every run afterwards. The exception is anyone whose company already pays for an agent suite, since the shortest path there is the one already switched on. ## Where to Go Next Each of the five categories has its own deeper page: Business and Enterprise AI Agents, AI Voice Agents, Workflow Automation Platforms, AI Agent Frameworks and Platforms, and Personal AI Agents. These related CodeWalkers pages are worth reading next: - [AI coding tools](/ai-coding-tools/): the agents that change code, ranked. - [Claude Code MCP and hooks](/ai-coding-tools/claude-code/mcp-and-hooks/): how agents get their tools. - [Terminal vs IDE agents](/ai-coding-tools/comparisons/terminal-vs-ide-agents/): two ways to work with a coding agent. - [Application security](/appsec/): scanning the code and packages that agents write and install. - [Vibe coding](/vibe-coding/): building apps by describing them to an AI. Product facts on this page were checked against vendor pages in September 2026. The hub carries no prices; each category page has its own. --- # Application Security ## Complete Guide to Aikido Security (2026) URL: /appsec/aikido/ Cluster: Application Security > Overview & Application Security Aikido connects code, dependency, secrets, infrastructure, cloud, container, domain, and runtime checks through one [application security service](/appsec/). Choose it when those checks can replace enough separate tools to make one platform worthwhile, including the work of reviewing its alerts. ## Fit at a Glance | Situation | Decision | | Several scanner contracts | Bundled platform trial | | Two-person evaluation | Developer plan | | Specialist SAST requirement | Matched scanner trial | | Strict local control | Architecture review | ## What Does Aikido Scan? Aikido documents one repository connection for dependency analysis, static analysis, secrets, licenses, infrastructure files, and malware checks. Other modules cover cloud assets, container images, domains, APIs, and runtime traffic, with plan-specific limits and setup. | Surface | What it analyzes | What it catches | | SAST | Application source | Code security findings | | SCA | Dependency graph | CVEs and licenses | | Secrets | Repository history | Exposed credentials | | IaC | Infrastructure files | Cloud misconfigurations | | Containers | Image layers | Package vulnerabilities | | CSPM | Cloud accounts | Asset misconfigurations | | DAST | Running endpoints | Runtime vulnerabilities | | Malware | Package installs | Malicious dependencies | | Licenses | Dependency inventory | License policy | | Runtime | Application traffic | Attacks and bots | A containerized API on AWS can put source, packages, images, infrastructure files, cloud assets, and a running endpoint into the same Aikido account. Confirm which module scans each asset and which plan provides the required enforcement before treating the connection as full coverage. ## How Does Aikido Reduce False Positives? Aikido AutoTriage reprioritises supported findings using code context, exploitability and its own rules. It mainly handles SAST findings, with selected cloud and container checks also supported. Review downgraded alerts before relying on that filtering. A matched trial should include an exploitable flaw, a safe lookalike, and a finding behind a non-production path. Record the original severity, the AutoTriage decision, the evidence shown, and the time needed for a reviewer to confirm or reverse it. ## How Does Aikido AutoFix Work? Aikido AutoFix turns eligible findings into proposed patches in an IDE or pull request. Review the diff, rescan the changed code and run the repository’s tests before merging. Aikido's current documentation covers fixes for dependencies, SAST, IaC, containers, and pentest findings. Review and test every generated change before it merges. What that looks like across different surfaces: - A SAST finding in your API layer produces a reviewable PR your team can inspect and merge - An IaC misconfiguration in your Terraform generates a targeted patch with an explanation - A vulnerable dependency surfaces with an updated version and a diff showing what changed - A container base image with unpatched CVEs gets a concrete remediation suggestion AWS reports that Aikido moved AutoFix from about 30 seconds per request to about five seconds by using Lambda tenant isolation. The isolation model gives each tenant dedicated execution environments and permits reuse only for later requests from the same tenant and function. AutoFix covers more than 100 vulnerability types. The current plan table lists 10 monthly fixes on Developer, unlimited fixes on Basic and Pro, and custom terms at Enterprise. Aikido says it does not use AutoFix code for model training. Its data page says it stores the original and fixed files involved in an AutoFix, while AWS documents same-tenant reuse inside the isolated Lambda boundary. ## What Does Aikido AI Pentesting Test? Aikido's pentest products send automated attack traffic to a running application and API, then produce a report for the selected scope. The current pricing page describes role-based access tests, multiple API styles, retesting, and several test depths. The public offer says the customer does not pay when the test finds no high or critical issue. Treat that as a commercial term to confirm in the order, including how severity, scope, retests, and disputed findings are handled. A standard assessment starts at $4,000 for one application and its primary APIs. The rightsized option scales with repositories, endpoints, roles, and application complexity, while continuous testing uses custom terms. Compare the quoted scope with the test method your audit or customer actually requires. Automated testing does not establish the experience, independence, or contractual scope of a human-led engagement. Keep a human test when regulation, customer terms, or the threat model requires it, and use automated testing for the documented cadence it can cover between reviews. ## How Do You Set Up Aikido? Set up Aikido by connecting a repository, then enable the scanners and integrations your team needs. The initial repository scan is agentless and read-only; cloud, runtime and active testing modules have separate access requirements. The platform documents integrations across these workflow categories: - Version control : GitHub, GitLab, Bitbucket, Azure DevOps - IDEs : VS Code and JetBrains, with real-time AutoFix in the editor as you write - Issue tracking : Jira, Linear - Compliance tooling : Drata, Vanta (relevant if you're targeting SOC 2 or ISO 27001) - CI/CD : Azure Pipelines, GitHub Actions IDE scanning can surface supported source, secret, infrastructure, and dependency findings before a pull request. Aikido also says those local scans use less repository and deployment context than cloud scans. Compare the IDE and cloud results before relying on the editor scan alone. Begin with one least-privileged repository connection. Record source-control permissions, webhook events, branch access, organization scope, and who can change a gate. Revoke the connection after the trial and confirm which findings, source-derived data, and audit records remain. Enable scanner modules separately. A repository scan, cloud connection, domain test, container registry, and runtime module require different data and permissions. Turning on one module at a time shows which feature produced a finding, added cost, or requested access. For every cloud connector, record the requested role, permitted accounts, regions, resources, and write capabilities. Run the first scan against a disposable or read-only scope, inspect the audit log, and remove any permission the documented check does not require. For a domain, API, or runtime module, define the authorized target and test window before activation. Confirm authentication, rate limits, exclusions, emergency disablement, retained request data, and the owner who can distinguish scanner traffic from a real incident. ## What Are Aikido’s Limits? Aikido’s scanners have language, deployment and plan limits. IDE scans have less context than cloud scans, and on-premises scanning does not provide a self-managed copy of the whole platform. Check each required scanner and deployment mode before replacing an existing control. The SAST engine uses Aikido rules plus customized open-source engines, with multi-file taint analysis limited to the languages named in its support table. IDE scanning has less repository and deployment context than the cloud scan. AutoTriage mainly applies to SAST, with only selected cloud and container checks supported. Local controls are also plan-specific. Pro adds on-prem scanning, while Advanced adds brokers for internal applications and a private registry proxy. Those features keep selected scanning or connectivity inside the customer's network; they are not a self-managed copy of the full platform. Teams with strict residency rules should map source, manifests, findings, telemetry, AutoFix files, and credentials before connecting a repository. The plan name alone does not answer that data-flow question. List the languages and assets you need to scan in a comparison table. Record whether each case runs in the IDE, cloud scan, pull request, release gate, or local control. A scanner name in the plan table does not prove identical rules, context, or enforcement at every surface. Run safe positive and negative fixtures for each required scanner. Preserve the rule identifier, evidence, severity, suppression option, gate result, and remediation. Record both missed problems and harmless code flagged as unsafe when deciding whether to buy. The Developer plan's 10 monthly AutoFix allowance is enough to test the workflow, while Aikido lists unlimited AutoFix on Basic and Pro. AI Pentesting and other resource-heavy features can use separate credits, so include those workloads in the trial budget. ## How Much Does Aikido Cost? Aikido offers a free Developer plan for two users. Basic starts at $300 per month for ten included users, while Pro and Advanced each display a $600 monthly starting price for ten users. Asset allowances differ, so compare the included limits as well as the headline price. Build the estimate from protected repositories, images, domains, cloud accounts, users, pentest scope, and any runtime modules. The same user count can produce a different plan when the protected-asset mix changes. Separate included features from usable capacity. A scanner may appear in a plan while its asset allowance, scan cadence, credit model, or deployment control prevents the intended rollout. Put each required module and allowance beside the quote. | Plan | Monthly price | Included scale | | Developer | $0 | 2 users, 10 repositories | | Basic | $300/10 users | 100 repositories | | Pro | $600/10 users | 200 repositories | | Advanced | $600/10 users | 500 repositories | | Enterprise | Custom | Negotiated scale | - Developer includes 2 images, 1 domain, 1 cloud account, and 10 AutoFix runs each month. - Basic includes 50 images, 3 domains, 3 cloud accounts, and unlimited AutoFix. - Pro includes 100 images, 10 domains, 10 cloud accounts, and unlimited AutoFix. - Advanced includes 200 images, 20 domains, 20 cloud accounts, and unlimited AutoFix. - Enterprise negotiates its modules, support, limits, and AutoFix allowance. Developer provides a two-user evaluation path with repository, asset, scan, and AutoFix limits. Basic adds workflow, reporting, and organization controls; Pro and Advanced change scanner availability and asset limits. Compare the plan price with the named controls it would replace. ## Who Should Use Aikido? Aikido suits teams that want one service for source, dependency, secret and cloud checks. A specialist may fit better when a specific language or deployment restriction determines the choice. The upgrade decision should follow evidence from representative repositories and assets. Record which scanner produced each finding, whether AutoTriage changed it, whether AutoFix generated a usable patch, and whether the configured gate enforced the policy. A broad scanner list still needs an owner for every queue the team keeps. ## Frequently Asked Questions ### Does Aikido train on your code? Aikido says it does not use AutoFix code for model training. AWS documents tenant-isolated Lambda environments for AutoFix, with reuse allowed only for later requests from the same tenant and function. Aikido separately says stored AutoFix data is limited to the original and fixed files, subject to its stated retention and secret-scanning controls. ### Does Aikido replace Dependabot? Aikido and Dependabot overlap on advisory-based dependency findings. Dependabot uses GitHub's dependency graph and advisory database and can open security-update pull requests. Aikido documents SCA alongside source, secret, container, infrastructure, and cloud scanners. Keep both only when each has a distinct owner and enforcement job. ### When is a paid Aikido plan worthwhile? A paid Aikido plan is worthwhile when its extra scanners, limits and workflow controls save more than the subscription costs. There is no universal team-size cutoff. Basic starts at $300 per month for ten included users; compare that cost with the tools it would replace and the time spent reviewing alerts. ### Can Aikido AI Pentesting replace a human pentest? Aikido AI Pentesting cannot replace a human pentest when a contract, audit or regulation specifically requires a human-led engagement. Its automated tests cover a defined application and API scope. Confirm that the test method, independence, report and retest terms meet your requirements. ## Verdict Choose Aikido when one contract and set of rules must cover code, dependencies, secrets, infrastructure, cloud assets, containers, domains, and selected runtime controls. AutoFix adds reviewable patches for eligible findings, while the repository's review and tests remain the approval gate. Keep a specialist when the requirement depends on an unsupported language, a package behavior signal, a specific self-managed architecture, or a test method Aikido does not document. Use the trial to check which requirements Aikido meets and whether its checks actually block the changes they should. Start with Developer on representative assets, then model Basic, Pro, or Advanced from the scanner modules and asset limits the team actually used. Replace existing tools only after the trial shows Aikido can do their required jobs and someone is responsible for each remaining alert. Before purchase, test integration removal and data deletion. Revoke source-control access, disconnect cloud credentials, remove a test user, and document what findings, source-derived data, generated fixes, and audit records remain. ## Where to Go Next - [Application Security hub](/appsec/) covers how the supply chain, secrets, SAST, and container security layers connect - [Socket.dev guide](/appsec/socket/) covers behavioral supply chain scanning in depth: the free tier, Socket Firewall, and reachability analysis - [Dependabot guide](/appsec/dependabot/) covers GitHub's built-in CVE-based dependency alerting, which runs alongside Aikido rather than instead of it - [AI code review hub](/ai-code-review/) covers the PR-level review layer for catching what your team writes before it merges ## Related - [Application Security hub](/appsec/) - [Socket.dev guide](/appsec/socket/) - [Dependabot guide](/appsec/dependabot/) ## Sources - [Aikido plans and included limits](https://www.aikido.dev/pricing) - [Aikido source-code scanning](https://www.aikido.dev/use-cases/secure-your-source-code) Facts and prices were checked against the official vendor pages on July 28, 2026. --- ## Top Application Security Tools Compared (2026) URL: /appsec/comparisons/ Cluster: Application Security > Overview & Application Security Choosing between two application security tools? Start with the comparison that matches the job you need done. Dependabot and Socket focus on dependencies. Snyk and Aikido cover a wider range of security checks. SonarQube puts code quality and security rules at the centre of the development process. If you have not made a shortlist yet, the [application security tools guide](/appsec/) explains the main options. This page helps you compare the tools already on your list. ## Which AppSec Tools Should You Compare? Compare Dependabot with Socket for dependency protection, Snyk with Aikido for a broader security platform, and Aikido with SonarQube when deciding between wider security coverage and code quality controls. | Comparison | Use It to Decide | Main Difference | | [Dependabot vs Socket](/appsec/comparisons/dependabot-vs-socket/) | Whether dependency alerts need extra package checks | Known advisories and update PRs versus package behaviour and install controls | | [Snyk vs Aikido](/appsec/comparisons/snyk-vs-aikido/) | Which platform fits your development tools and budget | Snyk's scanner products versus Aikido's bundled plans | | [Aikido vs SonarQube](/appsec/comparisons/aikido-vs-sonarqube/) | Whether you need broader security checks or code quality gates | Code, cloud and running-app checks versus code verification and a self-managed server option | These are not always either-or choices. Dependabot and Socket can work together, for example. Dependabot opens security updates for known vulnerable packages, while Socket can flag suspicious package behaviour before an advisory is published. ## How Should You Compare Application Security Tools? Compare security tools by what they check, where they run and how much they cost. Check that each tool supports your code and lets your team act on the problems it finds. ### Check What Each Tool Scans A tool that scans your source code does a different job from one that checks installed packages or tests a running website. Static application security testing (SAST) looks for flaws in the code you write. Software composition analysis (SCA) checks third-party dependencies. Dynamic application security testing (DAST) tests a running application. Check the languages, package managers and frameworks you use before comparing feature counts. Also check which plan includes each scan. SonarQube's dependency checks, for instance, require eligible Advanced Security access; they are not part of every base installation. ### Check When Each Tool Can Stop a Change An alert does not necessarily block anything. A pull request check needs an enforced merge rule to stop a change reaching the main branch. Install-time protection works earlier, before a package is installed. The [Socket guide](/appsec/socket/) explains the difference between its package information, repository checks and Firewall. The [Dependabot guide](/appsec/dependabot/) covers alerts, security updates and version updates. Keep those jobs separate when deciding which controls you need. ### Compare the Cost for Your Team Use your own repository count, contributors, code size and scan needs to work out the price. A low starting price may exclude the checks or hosting option you need. A free scanner may still need someone to maintain it and deal with its alerts. For the individual plans and limits, see the [Snyk guide](/appsec/snyk/), [Aikido guide](/appsec/aikido/) and [SonarQube guide](/appsec/sonarqube/). The head-to-head pages above explain how those differences affect the choice. ## How Can You Tell Whether a Security Tool Is Useful? A useful security tool finds real problems your developers can understand and fix. Try each shortlisted product on the same repository, with the same known issues and the plan you intend to buy. Count confirmed findings, missed problems and false alarms. Then ask a developer to follow a finding, make the fix and run the tests. A long alert list is not much help if nobody can work out what needs changing. Include setup time and the effect on pull requests. If a tool is meant to block a risky change, test that it actually does so. Keep a second scanner only when its extra findings or controls justify the extra cost and work. --- ## Complete Guide to Dependabot for Dependency Security (2026) URL: /appsec/dependabot/ Cluster: Application Security > Overview & Application Security Dependabot connects [GitHub's dependency graph](/appsec/) with advisory alerts, minimum-patch security pull requests, and scheduled version updates. Turn on alerts and security updates for every GitHub repository, add version updates through a `dependabot.yml` file, and add Socket when package behavior matters. ## At a Glance | What | Detail | | Features | Alerts, security updates, version updates | | Ecosystems | Feature-specific support matrix | | Pricing | Core features free | | Platforms | GitHub only | | AI layer | Agent-drafted fix pull requests | | Malware detection | Opt-in malware alerts for eight ecosystems | | Main limit | Advisory-based detection | - Alerts detect CVEs, Security Updates open fix pull requests, and Version Updates keep dependencies current. - Supported ecosystems vary across dependency graphs, alerts, security updates, and version updates. - Those three core features are free on every repository, while custom auto-triage requires GitHub Advanced Security and AI fixes also require a Copilot plan. - Dependabot supports GitHub repositories, with no GitLab or Bitbucket option. - Since April 2026, you can assign an alert to Copilot, Claude, or Codex for a draft fix pull request. - The separate malware alerts launched for npm in March 2026, now cover eight ecosystems, and remain opt-in rather than part of the CVE alert stream. - Unreachable vulnerability alerts create noise, and this CVE database layer does not scan malicious package behavior or zero-day threats. ## What Does Dependabot Do? Dependabot provides alerts for known vulnerable dependencies, security-update pull requests for available fixes, and scheduled version updates. You can enable these three features separately. ### Alerts Dependabot Alerts notify you when a dependency in your graph matches a known vulnerability in the GitHub Advisory Database. GitHub reviews advisories before publication. Alerts fire in two situations: when a new advisory is added for a package you already depend on, and when your dependency graph changes and the new state introduces a known vulnerability. Each alert shows the severity, the affected versions, the fixed version range, and the file where the dependency is declared. The alert also indicates whether the vulnerability is reachable from your code when that information is available, though reachability analysis is not always precise. Alerts are free on all repositories, public and private, on every GitHub plan. ### Security Updates Dependabot Security Updates are the automated follow-through on an alert. When a fix is available, Dependabot checks whether it can upgrade the vulnerable dependency to the minimum patched version without breaking the rest of the dependency graph. If it can, it opens a pull request for you. The key phrase is "minimum patched version." Dependabot does not bump you to the latest release. It moves you to the lowest version that resolves the advisory. That keeps breaking changes to a minimum and makes the PR easier to review and merge. A security update PR may include a compatibility score. That score is the percentage of CI runs that passed when updating between those specific versions, calculated from CI results across public repositories using the same change. It is context from other projects, not proof that the update passes this repository. Run the local test suite before merging. When Dependabot cannot open a security update, inspect the alert and dependency path before assuming there is no remedy. A version constraint, private registry, vendored dependency, grouped update, or lockfile conflict may prevent the automated pull request while a manual change remains possible. Keep the failed update attempt with the alert record. Note the resolver error, manifest, direct parent, available patched range, owner, and next review date. That evidence lets another maintainer continue the remediation without repeating the same dependency-graph investigation. Security Updates are also free on all repositories. ### Version Updates Dependabot Version Updates are the non-security layer. They keep your dependencies current even when no CVE is involved, and they run on a schedule you define in a `dependabot.yml` file committed to `.github/`. Here is a minimal starting configuration you can drop into `.github/dependabot.yml`: ```yaml version: 2 updates: - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly" groups: production-dependencies: patterns: - "*" exclude-patterns: - "typescript" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" ``` The `groups` key lets you combine related updates into a single PR rather than getting one PR per package. A broader group means fewer pull requests, but more changes to review or undo at once. GitHub Actions are a separate ecosystem with their own schedule. Review major-version changes, pin immutable commit SHAs where policy requires them, and run the repository checks before merge. Version Updates require you to commit the `dependabot.yml` file, and they are free on every GitHub plan. Dependabot applies a default three-day cooldown before opening version-update pull requests. The `cooldown` setting can change or disable that window when the repository's release policy needs a different delay. ## Can AI Agents Fix Dependabot Alerts? GitHub documents assigning a Dependabot alert to a supported AI coding agent. The agent receives the alert and repository context, then can open a draft pull request with a proposed fix. GitHub warns that AI-generated fixes may be incorrect. Treat the draft as untrusted code. Inspect the dependency diff, run the repository's checks and keep the pull request behind the normal review and branch-protection rules. This feature requires GitHub Code Security (the GHAS product that covers Dependabot custom features and code scanning) and a Copilot plan that includes coding agent access. It is not available on the free GitHub plan. Separately, Copilot Autofix applies to code scanning alerts rather than Dependabot alerts. It suggests targeted fixes for SAST findings flagged by code scanning, and it is also gated behind GitHub Code Security. ## Does Dependabot Detect Malicious Packages? Dependabot can detect package versions that match reviewed malware advisories when malware alerting is enabled. The feature is opt-in and separate from vulnerability alerts; it does not inspect every package for previously unknown malicious behaviour. When enabled, malware alerts appear as a distinct subcategory inside Dependabot alerts, separated from the CVE-based ones. That separation matters because the remediation is different. A CVE alert usually means bumping to a patched version. A malware alert often means the package itself is compromised and removal is the right move. The current feature is opt-in and supports configurable rules by malware type. GitHub's malware alert documentation lists Composer, Go, Maven, npm, NuGet, pip, RubyGems, and Rust as the supported ecosystems. That list is shorter than the dependency graph's ecosystem list, so check the malware page itself before assuming coverage for another registry. This covers package versions that match a reviewed malware advisory. GitHub says publication can take time because reports are reviewed first. Socket can add package-behavior signals during that interval, but those signals are evidence to review rather than a universal malware guarantee. ## How Do You Reduce Dependabot Alert Noise? Reduce Dependabot alert noise by checking dependency paths, prioritising exploitable findings and using narrowly defined auto-triage rules. Group version-update pull requests separately to reduce review volume; grouping changes the number of pull requests, not whether a vulnerability matters. Triage starts with the dependency path, affected version range, severity, exploit information, and available patch. Application ownership and call-path evidence then decide urgency. Apply the patch when it is compatible with the repository, so the vulnerable version is removed. Go teams can add `govulncheck` when symbol-aware reachability fits their build. npm teams can add Socket for package behavior, release signals, and install-time policy. These are different controls with different evidence, so neither is a universal equivalent for the other ecosystem. Measure noise on the repository instead of assigning a general quality score to Dependabot. Record how many alerts are direct, transitive, patched, exploitable, accepted, or blocked by an incompatible update. Use those results to decide which alerts need attention first. Keep one record for every dismissal or acceptance. Include the advisory, dependency path, current version, affected range, owner, compensating control, review date, and condition that reopens the decision. A free-text dismissal without an expiry can hide a changed dependency path or newly available patch. Test auto-triage rules against known alerts before organization-wide rollout. A rule that matches severity alone can suppress a direct dependency beside an unreachable transitive one. Use the narrowest documented conditions and preserve the alert history needed to review the result. Configure the default auto-dismiss rules for the documented low-risk cases, keep version updates active, and pair the advisory queue with a control that covers the remaining threat class. ## What Does Dependabot Miss? Dependabot can miss a malicious package before a matching advisory is published, and an update pull request does not prove that the application still works. Dependency alerts also do not replace source-code scanning or testing of the running application. The threat model it does not address well: - Malicious packages before advisory publication. A typosquat or compromised update can reach a lockfile before GitHub publishes a matching vulnerability or malware advisory. - Transitive unreachable vulnerabilities. Alerts fire on the full dependency graph, not on the code paths your application actually uses. - GitLab, Bitbucket, and self-hosted repos. Dependabot is GitHub-exclusive. Repositories hosted elsewhere need a separate update and security-alert workflow. Dependabot also does not prove that an update is compatible with the application. The compatibility score summarizes public-repository CI for a version pair, not this repository's build, runtime, or deployment. Keep local checks, review, and rollback evidence attached to every security update. Version Updates solve a maintenance problem rather than an advisory problem. Their schedule, grouping, ignore rules, cooldown, and open-pull-request limits change the review queue. Treat those settings as repository policy and review them when ownership, release cadence, or package-manager behavior changes. Test the handoff when no patched version exists. The alert still needs an owner, an exposure decision, a compensating control where possible, and a date to review the exception when the advisory or dependency graph changes. For the pre-advisory supply-chain threat class, Socket operates at the package behavior level and reports signals that may exist before an advisory is published. Running both keeps the advisory queue and package-risk evidence available at separate decision points. ## Is Dependabot Free? GitHub's security feature documentation confirms that core Dependabot alerts, security updates, and version updates remain available without buying GitHub Code Security. Paid controls sit on top of that baseline. | Feature | Free on all repos | Requires paid plan | | Dependabot Alerts | Yes | | | Default auto-dismiss (low-risk alert rules) | Yes | | | Dependabot Security Updates (auto-PRs) | Yes | | | Dependabot Version Updates (dependabot.yml) | Yes | | | Custom auto-triage rules | | GitHub Code Security (GHAS) | | AI agent fix assignment | | Code Security plus Copilot | | Copilot Autofix for code scanning | | GitHub Code Security (GHAS) | The default auto-dismiss behavior suppresses certain low-severity alerts before notification and is included without GitHub Code Security. The paid product adds custom rules that can snooze, dismiss, or trigger a security update under configured conditions. Test the rule against known alerts before applying it across an organization. ## Verdict Enable Dependabot as the GitHub baseline for known dependency advisories and update pull requests. Alerts and Security Updates cover a defined class of published vulnerability. Version Updates require a `dependabot.yml` file and can reduce version drift before an advisory arrives. Tune the queue with supported triage rules and repository evidence. Start by enabling the default auto-dismiss rules so low-risk alerts stop consuming attention. Group version update PRs by dependency type when that review unit matches the repository. Treat a low compatibility score as a signal to read the changelog before you merge the update. Review the first week of results with an assigned owner before expanding the rollout. Add Socket when package-behavior signals or supported install-time blocking are requirements. Keep Dependabot for GitHub advisory context and security-update pull requests. Dependabot is a GitHub product regardless of configuration. If your team works on GitLab or Bitbucket, consider Renovate for version updates and a dedicated SCA tool for security alerting. ## Frequently Asked Questions ### How do I enable Dependabot alerts? Enable Dependabot alerts from the repository’s Settings → Advanced Security page, or use an organisation security configuration. Check whether alerts are already enabled before changing the setting. Alerts do not need a dependabot.yml file; scheduled version updates do. ### Does Dependabot update GitHub Actions workflow files? Dependabot can update GitHub Actions references when dependabot.yml includes the github-actions ecosystem. Review major-version changes, use immutable commit SHAs where your policy requires them, and run repository checks before merging. ### Can I group Dependabot pull requests? Dependabot can combine related version updates into one pull request through the groups setting in dependabot.yml. Group by package name, dependency type or version-change type. Security-update grouping is configured separately, so confirm which update type each group applies to. ### Does Dependabot work on GitLab or Bitbucket? GitHub’s hosted Dependabot service does not directly manage repositories hosted only on GitLab or Bitbucket. Those repositories need a separate update and security-alert workflow, such as Renovate for version updates plus a suitable dependency scanner. ### What should I do if a Dependabot security update breaks my build? If a Dependabot security update breaks your build, leave the pull request unmerged, inspect the failed tests and changelog, and fix the incompatibility or choose another patched version. If the update has already merged, use your normal rollback procedure while keeping the vulnerability assigned for remediation. A compatibility score from other repositories does not replace your tests. ## Related - [Application Security hub](/appsec/) - [AI Code Review hub](/ai-code-review/) - [CodeRabbit guide](/ai-code-review/coderabbit/) ## Next - [Application Security hub](/appsec/) ## Sources - [Dependabot quickstart](https://docs.github.com/en/code-security/tutorials/secure-your-dependencies/dependabot-quickstart) - [Dependabot configuration reference](https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference) Updated September 24, 2026: Dependabot malware alerts now cover eight ecosystems instead of npm alone. --- ## GitHub Code Security and Secret Protection Guide (2026) URL: /appsec/github-advanced-security/ Cluster: Application Security > Overview & Application Security GitHub now sells two [application security products](/appsec/) for private repositories. Code Security contains CodeQL and dependency controls. Secret Protection contains secret scanning and push protection. Pick Secret Protection when leaked credentials are the main risk, Code Security when private code needs CodeQL, or both. ## At a Glance | What | Detail | | Products | Code Security; Secret Protection | | SAST engine | Semantic CodeQL queries | | Languages | 12 languages plus GitHub Actions | | Secret scanning | Repository content and history | | Push protection | Pre-repository secret blocking | | AI layer | CodeQL Copilot Autofix | | Public repositories | Core security features free | | Platform | Cloud and Enterprise Server | | Dependabot | [Separate Dependabot guide](/appsec/dependabot/) | - Code Security costs $30 per active committer each month, while Secret Protection costs $19. - CodeQL treats code as data and uses semantic, query-based analysis. - Language support covers C, C++, C#, Go, Java, Kotlin, JavaScript, TypeScript, Python, Ruby, Rust, Swift, and GitHub Actions. - Secret scanning covers commits, pull requests, issues, wikis, and the entire Git history. - Push protection blocks recognized secrets before they reach the repository. - Copilot Autofix suggests fixes for CodeQL alerts without requiring a separate Copilot subscription. - Public repositories get code scanning, secret scanning, push protection, and Copilot Autofix for free. ## What Does GitHub Advanced Security Include? GitHub Advanced Security includes two separately purchasable products. GitHub Code Security provides code scanning and dependency controls; GitHub Secret Protection detects exposed credentials and can block supported secrets during a push. GitHub Team and Enterprise customers can buy either product or both. | Product | Price | | GitHub Code Security | $30/active committer/month | | GitHub Secret Protection | $19/active committer/month | - Code Security includes CodeQL scanning, Copilot Autofix, custom Dependabot triage, security campaigns, and the dependency review action. - Secret Protection includes secret scanning, push protection, Copilot secret scanning, custom patterns, and push-protection bypass controls. You pay for unique committers whose commits were pushed to a repository with the feature enabled in the last 90 days. There's no pre-purchased seat count, so you don't pay for contributors who are inactive. ## How Does CodeQL Find Vulnerabilities? CodeQL finds vulnerabilities by building a database of source-code relationships and running security queries against it. Queries can follow data between inputs, functions and sensitive operations, then report matching flaws as code scanning alerts. CodeQL processes your codebase in three steps. It generates a structured database from your source code, capturing the relationships between variables, functions, calls, and data flows. It then runs a library of queries against that database. Each query looks for a specific vulnerability class: SQL injection, path traversal, command injection, insecure deserialization, hardcoded credentials, and more. The results surface as code scanning alerts in your pull requests and in the Security tab, with the specific line of code and an explanation of what the query found. CodeQL queries can follow data flow from an application input, across function calls and module boundaries, to a security-sensitive use. That capability does not guarantee fewer false positives in every repository. Run the query suite against representative code and measure which alerts survive your review process. ### Which Languages Does CodeQL Support? CodeQL covers C, C++, C#, Go, Java, Kotlin, JavaScript, TypeScript, Python, Ruby, Rust, Swift, and GitHub Actions workflows. If your stack sits outside that list, notably PHP and Scala, CodeQL does not analyse it. Check GitHub’s separate AI Scan support or another scanner rather than assuming CodeQL covers every language. ### Should You Use Default or Advanced CodeQL Setup? Use CodeQL default setup for a quick start with GitHub-selected languages, query suites and scan triggers. Use advanced setup when you need a custom GitHub Actions workflow, different queries or more control over the build and analysis. Advanced setup generates a customizable GitHub Actions workflow file. You get control over which queries run, what events trigger analysis, and how results are filtered. Advanced setup is worth the configuration cost if you need to run extended query suites, tune performance on large codebases, or exclude specific paths from analysis. GitHub also offers AI Scan for supported languages and frameworks beyond CodeQL. AI Scan and agentic autofix consume Code Security AI Credits, including on public repositories. Check the included allowance and spending budget before enabling those features widely. ## How Does Copilot Autofix Work? GitHub says Copilot Autofix generates fix suggestions for CodeQL alerts directly in the pull request where the alert appears. When a code scanning alert fires, Autofix reads the alert, the surrounding code, and the query documentation, then proposes a code change and explains what it's fixing and why. Autofix does not require a separate GitHub Copilot subscription, because it ships bundled with GitHub Code Security. The same alert may produce a different suggestion on different runs, and complex vulnerabilities that require tracing across multiple files may produce no suggestion at all. Treat Autofix suggestions as a starting point, because the PR still needs human review before it merges. Autofix supports C, C++, C#, Go, Java, Kotlin, JavaScript, TypeScript, Python, Ruby, and Rust across a subset of the default and security-extended query suites. ## What Does GitHub Secret Protection Detect? GitHub Secret Protection detects supported credential patterns and generic secrets in repository history and collaboration content. Secret scanning produces alerts; push protection can stop supported secrets before they reach the repository. The paid Secret Protection tier adds several capabilities the free tier doesn't include. Custom patterns let you define regexes for secrets specific to your organization. Validity checks verify whether a detected secret is still active against the issuing provider's API. Copilot secret scanning extends detection to unstructured secrets that don't match a specific pattern but look like credentials based on context. ### How Does GitHub Push Protection Work? GitHub push protection blocks pushes containing supported secrets before they reach the remote repository. It does not prevent local commits, so a developer may still need to remove the secret from local history before pushing again. When a developer pushes code that contains a recognized secret, the push fails with an explanation of what triggered the block. The developer then has three options: remove the secret and push clean code, mark it as a false positive with a documented reason, or mark it as test data. Contributors with write access can bypass the block with a reason logged for review. Organizations can configure delegated bypass, letting specific people approve bypass requests from others. Push protection covers command-line pushes, web editor commits, file uploads, and REST API requests, so the block applies regardless of how the developer is committing code. ## How Does Dependabot Fit into GitHub Advanced Security? Dependabot handles software composition analysis for GHAS. It watches your dependency graph, checks it against the GitHub Advisory Database, and opens automated fix PRs when a patched version is available. GitHub Code Security adds custom auto-triage rules on top, letting you write policies that auto-snooze, auto-dismiss, or auto-remediate alerts based on conditions you define. The AI agent fix assignment feature, where you can assign a Dependabot alert to Copilot, Claude, or Codex to draft a fix PR, also requires GitHub Code Security along with a Copilot plan. The Dependabot guide covers all three features (Alerts, Security Updates, Version Updates), alert triage, and the specific limits of advisory-database matching. If you're evaluating the Dependabot layer of this stack in any depth, read that guide alongside this one: [Dependabot guide](/appsec/dependabot/). ## How Do You Enable GitHub Advanced Security? Enable GitHub Advanced Security features from the repository’s Settings → Advanced Security page. Select the relevant paid product for private repositories, then configure code scanning, secret scanning and push protection. Organisation security configurations can apply settings across repositories. For code scanning, default setup needs no configuration file. GitHub detects your languages, selects the query suites, and starts running. You'll see the first alerts appear on the next push or within the first pull request you open after enabling it. Advanced setup generates a .github/workflows/codeql.yml file that you commit to the repository. Edit that file to control query suites, trigger events, and any path exclusions you need. For secret scanning, enabling it in settings is enough to start scanning new content. The historical scan of your Git history runs in the background and surfaces older alerts separately from new ones. ## What Does GitHub Advanced Security Miss? GitHub Advanced Security does not replace testing of the running application, and each scanner has language and detection limits. CodeQL reads source rather than observing live requests, so runtime behaviour and business rules still need dedicated tests even when static analysis finds related flaws. It also doesn't analyze code outside the languages it supports. If PHP or another unsupported language is part of your stack, CodeQL has no visibility there. Secret scanning catches credential formats it recognizes. Novel credential types or custom internal tokens won't trigger alerts until you configure custom patterns. The validity check feature tells you whether a detected secret is still active, but it depends on the issuing provider's API responding to the check. This guide covers security for GitHub repositories. Microsoft also offers GitHub Advanced Security for Azure DevOps as a separate integration. Repositories hosted only on GitLab or Bitbucket need a compatible scanner and reporting workflow. Push protection and secret scanning also don't replace a supply-chain behavioral scanner. They catch credentials your team accidentally commits. They don't catch a malicious package that was published to npm with an install script that exfiltrates your environment variables. For that threat class, add something like Socket alongside this stack. ## Which GitHub Security Features Are Free? Public GitHub.com repositories can use code scanning, secret scanning and dependency review without an active-committer licence. Private repositories need the relevant paid product. Some newer AI features consume separate credits even on public repositories. | Feature | Free on public repos | Requires paid plan | | CodeQL code scanning | Yes | Code Security | | Copilot Autofix | Yes | Code Security | | Secret scanning | Yes | Secret Protection | | Push protection | Yes | Secret Protection | | Validity checks | No | GitHub Secret Protection | | Custom secret patterns | No | GitHub Secret Protection | | Copilot secret scanning | No | GitHub Secret Protection | | Custom Dependabot triage rules | No | GitHub Code Security | | Dependabot agent assignment | No | Code Security plus Copilot | | Dependency review action | Yes | GitHub Code Security | Everything on that free list works at no cost for public repositories. For private repositories, you pay per active committer for whichever products you enable. ## How Much Does GitHub Advanced Security Cost? GitHub Code Security lists $30 per active committer per month, and Secret Protection lists $19. Both therefore cost $49 per covered active committer, before additional AI usage. For five active committers, that is $150 for Code Security, $95 for Secret Protection or $245 for both. At 50 active committers with both products enabled, the monthly bill is around $2,450. Compare that figure against a third-party SAST tool and a dedicated secret scanner. The native GitHub workflow still has value, but it stops being the cheapest option at some team size. ## Who Should Use GitHub Advanced Security? GitHub Advanced Security suits teams that want security findings and enforcement inside their existing GitHub workflow. Choose Code Security for code and dependency checks, Secret Protection for credential leaks, or both when both risks need coverage. A three-person team with both products enabled would pay about $147 each month at public list prices. That buys the documented CodeQL, dependency, secret, and push-protection features for covered repositories. Compare the result with the team's actual active-committer count and any unsupported languages before calling it complete coverage. If 40% of your code is in a language CodeQL doesn't support, you're paying for SAST coverage of 60% of the problem. Secret Protection is the relevant standalone product when credential leaks are the primary risk: organizations that handle API keys for external services, internal tooling teams that issue short-lived tokens, or any context where a leaked credential could affect customers. Push protection at $19 per committer is a concrete cost to address that category of incident. It can only block secrets that match supported or custom patterns, and an authorised user may be able to bypass the block. ## Frequently Asked Questions ### Is GitHub Advanced Security free for public repositories? GitHub.com public repositories can use code scanning, secret scanning, dependency review and standard Copilot Autofix without buying private-repository licences. AI Scan and agentic autofix use separate Code Security AI Credits, so public visibility does not make every AI feature free. ### What is the difference between GitHub Code Security and Secret Protection? GitHub Code Security covers CodeQL, Copilot Autofix, dependency review, and premium Dependabot controls. GitHub Secret Protection covers secret scanning, push protection, validity checks, generic secrets, and organization controls around bypasses. ### How does GitHub count an active committer? A GitHub committer counts when one of their commits was pushed to a repository with the paid product enabled during the previous 90 days, regardless of who performed the push. GitHub counts that person once across covered repositories in the organization or enterprise. ### Can GitHub Advanced Security replace Socket or Snyk? GitHub Advanced Security can replace overlapping checks from Socket or Snyk only if it covers your required repositories and risks. Socket adds package-behaviour and install-time controls; Snyk offers separate source, dependency, container and infrastructure scans. Compare those specific jobs before retiring either tool. ### Does Copilot Autofix require a separate Copilot subscription? Copilot Autofix for CodeQL alerts is included with GitHub Code Security and is free for public repositories. Assigning a Dependabot alert to a coding agent is a different feature and requires GitHub Code Security plus a Copilot plan with coding-agent access. ## Verdict Choose Code Security when GitHub-hosted repositories need CodeQL, dependency review, and related repository controls. Choose Secret Protection when credential detection and blocking are the priority. The products can be licensed separately, so the threat model should decide whether a private repository needs one or both. CodeQL alerts can appear in pull requests and repository security views. That saves developers from opening another dashboard, but it does not tell you whether the alerts are accurate or easy to fix. During a trial, check which alerts are valid, how long fixes take, and whether the team uses the results. The documented gaps include runtime behavior, unsupported languages, other hosting platforms, and package behavior outside advisory and dependency-review controls. Add a separate control only when the threat model names one of those gaps. For private repositories, the per-committer model lets a team license Secret Protection, Code Security, or both. ## Related - [Application Security hub](/appsec/) - [Dependabot guide](/appsec/dependabot/) - [Socket.dev supply chain guide](/appsec/socket/) - [AI code review hub](/ai-code-review/) ## Next - [Application Security hub](/appsec/) ## Sources - [GitHub Advanced Security features](https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security) - [GitHub licence and AI-credit billing](https://docs.github.com/en/billing/concepts/product-billing/github-advanced-security) Facts and prices were checked against the official vendor pages on July 28, 2026. --- ## Application Security URL: /appsec/ Cluster: Application Security > Overview & Application Security Application security means protecting the software you build and the people who use it. That includes checking your own code, the packages you install and the permissions your app gives users. It also means fixing problems after launch, when new vulnerabilities turn up. You do not need to buy every scanner on the market. You do need to understand which problems each check can find. Start with the risks in your app, then choose the tools and working practices that address them. ## What Is Application Security? Application security, usually shortened to AppSec, is the work of preventing, finding and fixing weaknesses in an application throughout its life. Scanning code is one part of that work. So are designing safe access controls, protecting passwords and API keys, testing changes and keeping software up to date. For example, a customer should only be able to open their own invoices. Checking that rule is application security, even if a scanner never flags the code. Checking an installed library for a known vulnerability is another part of the same job. The [Dependabot guide](/appsec/dependabot/) explains that second check and how automated update requests help. ## Why Does Application Security Matter? Application security helps prevent stolen data, unauthorised changes and service disruption. An app can pass its functional tests and still let the wrong person read a file or use an exposed credential. Small projects need these checks too, especially when they store customer details, take payments or connect to other services. The checks should fit the project. A public information page and a customer account system do not have the same risks. AI-generated code needs the same scrutiny as code you write yourself. A working feature is not proof that its permissions, dependencies or data handling are safe. ## What Do Application Security Tools Check? Application security tools check different parts of your software, including source code, installed packages, exposed credentials and running applications. Several tools can overlap, but a dependency scan cannot tell you everything about your own code. | Check | Checks For | Example | | Code analysis (SAST) | Unsafe patterns in code without running the app | User input reaching a database query unsafely | | Dependency analysis (SCA) | Known vulnerabilities in third-party components | An installed library with an available security fix | | Package analysis | Suspicious behaviour in dependencies | An install script attempting unexpected network access | | Secret scanning | Exposed credentials and tokens | An API key committed to a repository | | Dynamic testing (DAST) | Weaknesses in a running application | A web endpoint responding unsafely to unexpected input | SAST means static application security testing; SCA means software composition analysis. DAST means dynamic application security testing. The names matter less than the distinction between reading code, checking components and exercising an app. Container and infrastructure checks cover another part of the picture, such as vulnerable software in a container image or an overly permissive deployment setting. Monitoring and runtime protection help detect or block attacks after release. None of these removes the need to test who can access sensitive functions. ## Which AppSec Guide Should You Start With? Start with the AppSec guide that addresses the problem you need to solve. Dependency updates, suspicious packages, code analysis and combined scanning services serve different purposes, so there is no useful single ranking for all of them. - [Dependabot](/appsec/dependabot/) explains GitHub dependency alerts and automated update pull requests. Start here if you need to keep track of vulnerable packages. - [Socket](/appsec/socket/) covers suspicious package behaviour and supply-chain checks. Read it when you need to look beyond known vulnerability reports. - [GitHub Advanced Security](/appsec/github-advanced-security/) explains GitHub's code and secret scanning products, their limits and when private repositories need paid features. - [Snyk](/appsec/snyk/) covers code and dependency checks within developers' existing workflows, including editors and automated builds. - [SonarQube](/appsec/sonarqube/) explains code analysis and quality gates, including the choice between a cloud service and a server you manage. - [Aikido](/appsec/aikido/) covers a service that brings several security checks together. Read it if you want to manage findings from different scanners in one place. If you already have two options in mind, use the [application security tool comparisons](/appsec/comparisons/). The [Dependabot and Socket comparison](/appsec/comparisons/dependabot-vs-socket/) is particularly useful for understanding why two package checks can complement each other. ## How Do You Add Security Checks to Development? Add security checks to development by identifying what needs protection, enabling relevant checks and deciding who will fix the problems they find. Start with one repository you understand before rolling the same settings out everywhere. 1. Identify sensitive data and actions. List what users can read, change or delete. Check how the app enforces those permissions. 2. Check dependencies and credentials. Review package alerts and prevent secrets from entering the repository. If a key has leaked, revoke or rotate it; deleting the line alone is not enough. 3. Add code checks that support your language. Run them where developers can act on the results, such as their editor or pull requests. 4. Test the running app. Check important user journeys and access restrictions in an authorised test environment. Use automated dynamic testing where it fits. 5. Review findings and verify fixes. Prioritise problems by their potential impact and whether the affected code is used. Test the fix before merging it. 6. Keep checking after launch. New dependency vulnerabilities and configuration changes can affect code that has not changed. Maintain alerts, monitoring and a clear response process. Do not make every warning block a release on day one. First check that the results are useful, then configure required checks for the problems your team has agreed must stop a merge. A scanner that reports a problem does not automatically prevent that code from being shipped. ## How Does AppSec Scanning Differ from AI Code Review? AppSec scanners apply security checks to code, dependencies and other parts of an application. AI code reviewers examine proposed changes and may identify logic mistakes, missing checks and security flaws. Their findings can overlap, but neither replaces the other. The distinction is not simply when they run. Both can report on a pull request, and some security tools also run in an editor or on a schedule. Use the [AI code review guide](/ai-code-review/) to understand what review assistants add alongside security scanning. ## What Can Security Scanners Miss? Security scanners can miss flawed permissions, business rules and vulnerabilities outside the languages or components they analyse. A clean report means the configured checks found nothing; it does not prove the application is secure. Scanners can also report genuine vulnerabilities that are difficult to exploit in your particular app. Investigate how the affected component is used before deciding what to fix first. Keep the reason for dismissing an alert so another developer can understand the decision later. Give each alert queue an owner and remove duplicate reports where possible. Buying another scanner will not help if nobody has time to work through the findings you already have. ## What Affects Application Security Costs? Application security costs depend on the tools you use, the amount of code or number of developers covered, and the time spent investigating and fixing problems. Free tiers can get a small team started, but private repository support and organisation-wide controls may cost extra. Check the billing unit before comparing prices. A developer licence, a plan covering a fixed number of users and a charge based on code size are different purchases. Minimum seat counts and annual commitments can also change the total. The individual tool guides explain those terms alongside each product's limits. Self-hosting adds server maintenance and upgrades to the work your team must do. A managed platform can reduce that work, although you may still need a separate tool for a check it does not provide. Compare the time your team will spend as well as the subscription. ## Frequently Asked Questions ### Who is responsible for application security? Developers, security specialists and operations teams share responsibility for application security. Developers fix code and dependency problems, security specialists help assess risks, and operations teams maintain deployment controls and monitoring. In a small team, one person may do several of those jobs, but each reported problem still needs an owner. ### Can a small team start application security without buying tools? A small team can start application security with free dependency alerts, code scanning where a suitable free tier is available, and careful code review. Free software does not remove the time needed to configure checks, investigate warnings and test fixes. Private repositories and team features may require paid plans. ### Can one AppSec platform replace every security check? One AppSec platform can bring several scanners together, but it cannot replace secure design, access checks and human review. Check whether the platform supports your languages, dependencies and deployment before adding specialist tools for any missing checks. ## Where Should You Start With AppSec? Start AppSec work with one application and the data or actions that would cause the most harm if exposed. Review its access controls, dependencies and credentials, then add code checks and tests that fit the way you build it. For a GitHub project with no dependency monitoring, the [guide to Dependabot alerts and updates](/appsec/dependabot/) is a practical first step. If those checks are already running, use the [AppSec comparisons](/appsec/comparisons/) to decide whether another tool addresses a problem you still have. --- ## Complete Guide to Snyk Application Security (2026) URL: /appsec/snyk/ Cluster: Application Security > Overview & Application Security Snyk is an [application security platform](/appsec/) for open-source dependencies, first-party code, container images, and infrastructure as code. Its controls can run in an editor, a repository integration, or CI, while the plan and test quota vary by product. Choose it when one account must cover several of those jobs, and a narrower tool when you need one specialist control. ## Fit at a Glance | Requirement | Snyk Surface | | Dependency CVEs | Open Source | | First-party code flaws | Code | | Image vulnerabilities | Container | | Cloud misconfiguration | IaC | | Private connectivity | Broker | | No source upload | Deprecated Local Engine | ## What Does Snyk Scan? Snyk’s four established scanners check dependencies, application source, container images and infrastructure configuration. Each examines different files and produces a different kind of finding. Snyk also offers separate API and web testing and secrets capabilities; confirm their subscription terms rather than assuming every product is included. | Scanner | Reads | Produces | | Open Source | Manifests and lockfiles | Vulnerabilities and licenses | | Code | First-party source | Code security findings | | Container | Images and metadata | Image vulnerability findings | | Infrastructure as Code | Cloud configuration files | Misconfiguration findings | These scanners share the Snyk account and integrations, yet they produce findings with different owners and remedies. A vulnerable library needs an upgrade decision. An unsafe data flow needs a code edit. A public storage bucket needs a configuration change and a check of the deployed resource. ## How Does Snyk Find and Fix Code Flaws? Snyk Code finds supported source-code flaws through semantic analysis, including data flow across files for supported languages. Agent Fix can then propose changes for eligible findings and rescan them. Ruby is an exception to interfile analysis in Snyk’s current language documentation. Snyk says its code engine models more than 25 million data-flow cases, uses more than 35,000 vulnerability examples and fixes, and produces fixes with 80% accuracy in its own testing. Test those vendor figures with vulnerable fixtures from the languages the team ships. Snyk Agent Fix can produce up to five candidate edits for an eligible finding. Snyk Code checks the proposed change again, and the developer can review the diff before applying it. Eligibility depends on the issue and language. Autofix shortens remediation only when the suggestion preserves behavior and removes the reported flaw. Trial it with a test suite, a security regression test, and manual review of the data flow. A plausible diff without a passing exploit test remains a suggestion. ## Where Can You Run Snyk Scans? You can run Snyk through repository integrations, IDE extensions, the CLI, CI jobs and supported APIs. GitHub, GitLab, Bitbucket and Azure Repos integrations can import projects and provide pull request checks; the available scan and fix features depend on the product. IDE plugins move selected findings into the developer's current editing session. Snyk documents extensions for Visual Studio Code, Visual Studio, and JetBrains IDEs. The plugin relies on the CLI and Snyk vulnerability data for many functions, which means local authentication, proxy settings, and workspace trust still need an owner. CI integrations can enforce a policy gate and export results. A typical split keeps fast source checks in the IDE, runs changed-code or dependency checks on a pull request, and schedules default-branch monitoring for newly disclosed vulnerabilities. Scan frequency counts against the plan differently for each product. The CLI brings the same scan families to a local shell or pipeline. Its Open Source command uses `snyk test`, while source scanning uses `snyk code test`. Container and IaC each have their own test command, which keeps the input and failure meaning explicit. Snyk warns that its CLI may invoke package managers to collect dependency data, and a malicious configuration can execute code. Unknown repositories belong in an isolated runner with limited credentials. ## How Does Snyk Prioritise Vulnerabilities? Snyk prioritises dependency vulnerabilities using reachability and other risk information. Reachability checks whether application code can call vulnerable dependency code; its call-path evidence helps reviewers decide which alerts need attention first. Current general-availability coverage includes Java with Maven or Gradle, JavaScript and TypeScript with npm, Yarn, or pnpm, and Python with pip, Poetry, or Pipenv. C# with NuGet or Paket is listed as early access. The CLI and several CI paths also carry early-access status, while common repository integrations are generally available. Reachability should reorder work rather than erase the advisory. A reachable vulnerability gets urgent review because the call path exists. An unreachable result can lower its place in the queue, while a later code or dependency change may alter that path. Snyk's Risk Score combines reachability with other context. Keep the raw severity, exploit data, asset exposure, and reachable call path visible during triage. One score can help sort the queue, but keep the underlying evidence when deciding to accept or dismiss a risk. ## Where Does Snyk Process Your Code? Snyk is cloud-first, with multi-tenant SaaS, managed single-tenant private cloud, and Snyk Broker between the platform and private resources. Snyk Broker is limited to Enterprise customers. The customer runs the Broker client inside its network, while Snyk runs the server side. Repository credentials stay within the customer network, and an approved data list limits what the SaaS service can request. Write a data-flow record for every scanner enabled in the trial. Include source files, manifests, lockfiles, container metadata, requests, responses, credentials, findings, telemetry, and generated fixes. Record where each item is processed, stored, and deleted rather than applying one answer to the whole platform. Test the Broker allowlist, outbound destinations, credential revocation, and behavior when the service or Broker is unavailable. Broker-assisted access does not turn the SaaS platform into a self-managed deployment. Snyk Code Local Engine keeps analysis local, but it is deprecated and scheduled for removal in a future release. Snyk says existing customers will receive support through their current contracts. Do not design a new deployment around it; use SaaS, Broker, or another supported architecture that meets the source-handling rule. Data review should follow the scan type. Snyk Open Source needs manifest, lockfile, and dependency metadata. Snyk Code requires enough source context to run its analysis. Container integrations inspect image contents, while IaC repository scans access infrastructure files for the duration described in Snyk's data policy. ## What Are Snyk’s Limits? Snyk's product breadth creates separate test counts and separate purchase choices. One Open Source scan can consume a test for each manifest where vulnerabilities are found. Code counts repositories, Container counts images or container files, and IaC counts configuration files. That accounting can make a monorepo behave differently from a small single-manifest service. Inventory repositories, manifests, container images, and infrastructure files before estimating the free allowance. Then run a normal week of pull requests and scheduled scans to measure actual usage. The test-count page also separates these plan limits from credit-based licenses. Customers on a negotiated credit contract need the applicable license document rather than the public Free or Team table. Record how many scans you expect, how renewal is priced, and what happens when you exceed the allowance before comparing annual costs. Language and integration coverage also varies by product. Check Snyk's support matrix for each language, package manager, and scan path. A language listed on Snyk’s website may not be supported by every scanner or integration. Snyk Open Source documents known vulnerabilities and license issues as its core job. Teams that require pre-advisory package behavior or install-time blocking should test a dedicated supply-chain control beside Snyk and compare the evidence. The [Socket guide](/appsec/socket/) explains that separate threat window. Claims about false-positive rates need proof from the team's own repositories. Use the same vulnerable and safe fixtures across candidate tools, record missed findings, and review how much context each alert gives the developer. The best scanner on a product page can still be the wrong scanner for a framework or custom sanitizer. ## How Much Does Snyk Cost? Snyk currently lists a Free plan, Team starting at $25 per month, and a quote-based Enterprise platform subscription. Free and Team have project and test limits; Enterprise uses credits across capabilities. Confirm the units and included products in your order rather than applying an older per-developer price to every plan. | Plan | Starting Price | Plan Scope | | Free | $0 | 5 projects; core scanners | | Team | $25/month starting price | 100 projects; higher limits | | Enterprise | Custom | Credit-based; quote required | Free includes five projects and access to Code, Open Source, IaC and Container. The current table lists 100 Code tests per month. Team covers up to ten developers, 100 projects and 1,000 Code tests per month; check the account for the other products’ allowances. Enterprise customers buy credits for the capabilities they use. Code and Open Source are each listed at one credit per active contributor per day, while Container and API & Web use image and target-based units. Compare expected usage across products before signing the quote. ## How Should You Evaluate Snyk? Evaluate Snyk on one representative repository by checking whether it finds known flaws, produces useful fixes and fits your scan budget. Include safe examples for each product you plan to use, then compare valid findings, false positives and missed cases. Keep each fixture safe and explicit. Use a non-executable vulnerable dependency example, a controlled source data flow, an intentionally weak container declaration, and a reversible infrastructure error. Never import live malware or real credentials to prove a feature. Save the product, plan, integration, command, policy, raw result, rule identifier, dependency path, and proposed remediation for each run. Repeat the case after a configuration change. That record helps you tell a missed issue from a configuration error, a suppressed finding, or a feature missing from your plan. Finish with an offboarding test. Remove the integration, revoke tokens, disable the Broker route, and ask what source-derived data and findings remain. Record the deletion path and export format beside the technical score so a future tool change does not strand the security history. Have engineering and security review the same sample independently. If reviewers disagree about severity, reachability, ownership or the fix, resolve those questions before making Snyk part of the team's routine. 1. Map the assets. Count private repositories, manifests, image builds, and IaC files that will consume tests. 2. Check coverage. Verify each language, package manager, and integration in Snyk's current support matrix. 3. Isolate unknown code. Run CLI scans of untrusted repositories on a runner without production credentials. 4. Measure the signal. Record true findings, false positives, missed fixtures, and time to a verified fix. 5. Test remediation. Apply an Agent Fix or upgrade pull request only after tests prove the change preserves behavior. 6. Model the bill. Use the observed test count and 90-day contributor count when requesting the final quote. The evaluation should end with one owner for policy, exceptions, and recurring scans. A dashboard without a remediation path becomes another alert queue, regardless of how many scan types feed it. ## Verdict Choose Snyk when one account must place dependency, source, container, and infrastructure findings in documented repository, IDE, CLI, and API workflows. Reachability availability, remediation paths, and enforcement points vary by product, language, integration, and plan, so verify each required surface in the trial. Before buying, check which products you need, how many tests they will use, and how many developers you will be billed for. Start with Free on representative repositories, verify every claimed language and integration, and calculate the subscription cost for your measured usage and contributor count. Choose a specialist tool if you only need one type of security check, or move to a paid Snyk plan when the combined workflow proves worth the added cost. ## Frequently Asked Questions ### What does the Snyk Free plan include? Snyk Free includes five projects and access to Code, Open Source, IaC and Container scanning. The current pricing table lists 100 Code tests per month. Check the product-specific allowances in your account before scheduling repeated scans. ### How does Snyk count a contributing developer? Snyk’s contributing-developer documentation counts people who committed to monitored private repositories during the prior 90 days, excluding public open-source contributions. Check how that definition applies to your subscription: current Enterprise pricing uses active-contributor credits, while the public Team offer is presented separately. ### Can Snyk scan code without uploading it? Snyk Code Local Engine keeps code analysis local, but Snyk has deprecated it and says it will be removed in a future release. Existing customers receive support through current contracts. New deployments should use Snyk SaaS, Snyk Broker for private connectivity, or another supported architecture that meets the source-handling requirement. ### Does Snyk CLI ever execute project code? Snyk warns that its CLI may invoke package managers to collect dependency data. A malicious project configuration could execute code during that step. Scan only trusted working directories, use an isolated runner for unknown repositories, and review the command's code-execution warning before adding it to shared automation. ### Which languages support Snyk reachability analysis? Snyk currently documents general availability for Java with Maven or Gradle, JavaScript and TypeScript with npm, Yarn, or pnpm, and Python with pip, Poetry, or Pipenv. C# with NuGet or Paket is listed as early access. Integration status also varies by scanning method. ## Related - [Application Security hub](/appsec/) - [Socket guide](/appsec/socket/) - [SonarQube guide](/appsec/sonarqube/) - [Snyk vs Aikido](/appsec/comparisons/snyk-vs-aikido/) ## Sources - [Snyk plans and credit rates](https://snyk.io/plans/) - [Snyk documentation](https://docs.snyk.io/) Facts and prices were checked against the official vendor pages on July 28, 2026. --- ## Complete Guide to Socket.dev for Supply Chain Security (2026) URL: /appsec/socket/ Cluster: Application Security > Overview & Application Security Socket analyzes package risk before a [dependency](/appsec/) reaches a project or build. Its package intelligence, pull request checks, and install-time firewall address the exposure window between a new release and a public advisory. Run it beside Dependabot, which keeps the advisory queue, and pay for reachability only after a trial shows it removes review work. ## Fit at a Glance | Situation | Starting Point | | Exact package research | CLI deep score | | GitHub dependency change | Pull request scan | | Local install protection | Firewall Free | | Private registry policy | Firewall Enterprise | | Known vulnerability queue | Dependabot plus reachability | ## What Does Socket Analyse? Socket's alert catalog documents more than 80 dependency risks across supply chain, vulnerability, quality, maintenance, and license categories. Each finding has a severity and a policy action, so an organization can ignore, monitor, warn, or block a specific issue type. The supply-chain group includes known malware, possible typosquats, obfuscated files, install scripts, telemetry, unstable ownership, and packages fetched through HTTP. These are documented signals rather than proof that every flagged package is hostile. A build tool can use a shell or write files for valid reasons, while the same capability inside a tiny typo package deserves immediate inspection. Socket records package capabilities alongside risk and quality scores. The report can show environment-variable access, network access, shell calls, filesystem access, and native code. That evidence helps a reviewer ask a useful question: does the dependency need this capability for the job you are giving it? Known malware follows a separate confirmation path in Socket's threat data. Socket says entries in its Threat Feed have been confirmed by a human reviewer. Optional AI-detected risk settings trade faster warnings for some false positives, so policy owners should keep those findings distinct from confirmed malware. ## How Do Socket Package Scores Work? The Socket CLI separates shallow and deep scores. A shallow score covers code published by the named package. A deep score includes the whole shipped tree, including direct dependencies and every transitive dependency below them. That split matters because a healthy top-level package can inherit a poor maintenance score or a high-severity alert several levels down. The deep report identifies the package responsible for each lowest category score and lists top alerts with their severity. Read the source of the drop before reducing the decision to one overall number. | View | Scope | Use | | Shallow | Named package | Publisher code | | Deep | Full dependency tree | Release approval | | Alerts | Issue evidence | Manual review | | Capabilities | Observed access | Threat fit | Socket also warns that scoring changes over time. Store the evidence used for a dependency decision, including the exact package version, alert list, and score date. A screenshot of one old score should never become permanent approval. ## How Do I Check a Package Before Updating It? I use Socket before changing a manifest, rather than waiting for a pull request check to object afterward. The candidate must be a stable release that has been public for more than 24 hours. That delay gives registry metadata, advisories, and package analysis time to settle before the version reaches a project. Run this preflight as one approval checklist, and keep the output with the dependency change: 1. Find the latest stable release and confirm it has been public for more than 24 hours. 2. Score the exact version, including the deep result for direct and transitive dependencies. 3. Reject critical or high alerts in the package itself or anywhere in the shipped tree. 4. Compare every score category with the current pin and investigate a drop greater than 15 points. 5. Review install scripts, network access, environment access, native code, and ownership changes. 6. Check the repository's open Dependabot alerts and the advisory for the candidate version. 7. Read the manifest and lockfile diff, then run the project's normal tests before approval. Socket accepts ecosystem coordinates and package URLs, so the same check can cover npm, Python, and Maven packages without treating the CLI as npm-only. ```bash socket package score npm @ socket package score pypi/@ socket package score pkg:maven/:@ ``` One minor update once dropped a supply-chain score from 98 to 53 without a critical or high alert. That result is why I check score changes even when there are no critical or high alerts. A capability finding still needs a reasoned check. An install script should point to readable package code and a job the package must perform. Network or environment access should match the documented feature, while an ownership change needs confirmation from the publisher's repository and release channel. I trace a transitive alert to the dependency that caused it and look for a parent release that removes or upgrades that dependency. A package passes only when the exact version has zero critical or high Socket alerts across self and transitive code, no unresolved advisory, and no unexplained score collapse. ## How Does Socket Differ from Dependabot? Dependabot identifies published dependency advisories and opens update pull requests. Socket adds package-behaviour signals, release checks and install-time blocking through Firewall. Running both can cover known vulnerabilities and suspicious package changes without treating either as complete protection. | Workflow Job | Dependabot | Socket | | Known advisory alert | Primary | Included signal | | Scheduled update | Primary | No | | Package capability review | No | Primary | | Install-time block | No | Firewall | | Pull request policy | Update rules | Risk rules | Dependabot security updates target the minimum version that fixes a known vulnerability when the dependency graph can resolve it. Socket's policy check answers a separate approval question about the candidate release and its full tree. I keep both controls active across every repository that carries third-party packages. Dependabot owns the advisory queue and update pull requests. Socket owns the pre-change score, the dependency-risk check on a pull request, and the evidence behind any override. ## How Does Socket Firewall Block Packages? Socket Firewall sits between a package manager and its registry. It extracts the package name and version, checks Socket's security data, then allows the download or returns a blocking error. The control applies before package code reaches the machine. Firewall Free runs as a command wrapper with no Socket account or configuration file. Socket documents npm, yarn, pnpm, pip, uv, and Cargo among its core package-manager coverage. It blocks known malware and warns about AI-detected threats on public registries. Firewall Enterprise adds organization policy, private registry coverage, allow lists, dashboard events, and centrally managed deployment. Its documented modes cover a local wrapper, a persistent registry interface, and an HTTP or HTTPS proxy. The proxy support matrix varies by package manager, so check the exact ecosystem before choosing a deployment mode. Keep the package manager's own execution controls in place. For pnpm, install-script approval decides whether a dependency may run its lifecycle script. Firewall decides whether the package should be downloaded under the current Socket policy. Install-script approval controls code execution; Firewall controls the download. Check both results. ## How Does Socket Check Vulnerability Reachability? Socket reachability analysis checks whether vulnerable dependency code can be called. Team provides precomputed reachability, while Enterprise adds application-level function analysis. Socket advertises reduced CVE false positives for these features; verify that benefit against your own alert queue. Treat those percentages as vendor claims to test against your own alert queue. Export a representative set of known-vulnerability findings, record which ones Socket marks reachable, and have an engineer verify the call paths. The result should change triage order without hiding a finding that still needs an owner. Reachability results can change when an application's call paths change. A vulnerable function that appears unreachable today can become reachable after an application change. Keep advisory monitoring active and rescan after dependency or call-path changes instead of closing the underlying risk forever. ## Which Package Ecosystems Does Socket Support? Socket's package search covers npm, PyPI, Maven, Go, NuGet, RubyGems, Cargo, Composer, Hugging Face, Chrome Web Store, and Open VSX. Product coverage differs by surface, so package search, CLI scores, GitHub scans, and Firewall should be checked separately. | Ecosystem | Registry | | JavaScript | npm | | Python | PyPI | | Java and Kotlin | Maven | | Rust | Cargo | | Ruby | RubyGems | | .NET | NuGet | | PHP | Composer | The command should name the ecosystem and exact version when ambiguity is possible. A package URL makes that identity explicit, which is useful for scoped npm packages and Maven coordinates. Save the exact command with the review evidence so another developer can reproduce the decision. ## What Are Socket’s Limits? Socket cannot tell from a package capability alone whether that capability is malicious. Install scripts, native code and network access can all be legitimate. Your policy and review determine which findings block a package and which need a documented exception. Set separate rules for findings detected by AI. Socket's official threat-feed explanation says potential AI findings can arrive before human confirmation and may include false positives. Teams that need the fastest warning can enable them as review items without treating each one as confirmed malware. Socket also leaves other security surfaces untouched. It doesn't replace first-party static analysis, dynamic testing, a secrets manager, or a human review of a dependency with broad machine access. Decide which tool handles each check and who acts on its results. ## How Much Does Socket Cost? Socket has a Free plan, Team at $25 per developer per month, Business at $50 per developer per month and custom Enterprise pricing. New Team plans start at five seats, making the monthly minimum $125 before annual discounts. New Business plans start at twenty seats. | Plan | Monthly Price | Included Scale | | Free | $0 | 1,000 scans; malware blocking | | Team | $25/dev | 5,000 scans; reachability | | Business | $50/dev | Unlimited; SSO and SBOM | | Enterprise | Custom | Unlimited; function reachability | | Open source | $0 | Offer terms | Free documents 1,000 scans each month, unlimited developers and repositories, one repository label, and malicious-dependency blocking. Socket's current page gives conflicting Free membership limits. The plan card says three members while its comparison table says five. Confirm that limit inside the product instead of budgeting from either number. Business removes the scan and member caps, then adds SSO or SAML, software bill of materials exchange, compliance integrations, and scanning for GitHub Actions and AI models. Enterprise adds more source-control platforms, full function-level reachability, and organization-wide Firewall options. New Socket Team subscriptions start at five developer seats, so the listed $25-per-developer rate costs at least $125 a month before an annual discount. Existing subscriptions below the current minimum retain their existing seat floor. Run the trial against the alert volume your team already has. Pay for reachability when the verified reduction in review time covers the subscription, rather than buying it from the percentage on the pricing page. ## How Do You Add Socket to Pull Request Checks? Add Socket to pull request checks by running `socket ci` in your CI workflow and configuring its policy result as a required check when it should block merges. Dependency-change checks can run when a manifest or lockfile changes; keep ongoing advisory monitoring active separately. ```bash pnpm exec socket ci ``` When a finding fails the check, open the package report before changing policy. Identify whether the alert belongs to the named package or a transitive dependency, confirm its severity, and inspect the capability or advisory behind it. Replace the dependency or record a narrow override with an owner and expiry. A useful review comment includes the exact version, release age, shallow and deep critical or high counts, score movement from the current pin, advisory result, and tests run. That record lets the next update start from evidence instead of repeating the same investigation. ## Verdict Socket earns a place in a dependency workflow when package behavior and release signals must be reviewed before a new version enters the tree. The free package search and CLI are useful for exact-version research, while the GitHub check turns the same evidence into a merge policy. I would start with package scores, the 24-hour release-age rule, zero critical or high self and transitive alerts, and the existing Dependabot queue. Add Firewall when install-time blocking matches the threat model. Pay for Team reachability only after a trial proves that it removes enough false-positive work from the repository's current advisory backlog. ## Frequently Asked Questions ### Is Socket free for open source? Socket provides free access for open-source projects, and its pricing page says maintainers can request a Team account. Package search also remains available for public research. Check the current offer before planning a private-repository rollout because private team controls follow the normal plan limits. ### How does Socket work with Dependabot? Dependabot alerts track vulnerable dependencies through GitHub's dependency graph and advisory database. Socket adds package behavior, publishing signals, install-time controls, and policy checks. Keep Dependabot alerts and security updates enabled, then use Socket to inspect risks that need package-level evidence or an earlier blocking point. ### Does Socket document typosquat detection? Socket documents possible typosquats among its supply-chain risks. The signal can warn or fail a check according to policy, but the name match still needs review. Confirm the publisher, repository, release contents, and intended package name before approving an override. ### What does the Socket CLI deep score include? The Socket CLI deep score covers the named package plus its direct and transitive dependencies. It reports the lowest contributing scores, capabilities, and top alerts across that tree. The shallow score describes the package itself, so read both views because a clean package can still bring in a risky dependency. ### Can Socket Firewall protect pnpm installs? Socket documents pnpm among the package managers covered by Firewall. The free wrapper checks public-registry installs and blocks known malware, while Enterprise adds organization policy, private registries, allow lists, and managed deployment modes. Keep pnpm's install-script approval enabled as a separate execution control. ## Related - [Application Security hub](/appsec/) - [Dependabot guide](/appsec/dependabot/) - [AI code review hub](/ai-code-review/) ## Next - [Dependabot vs Socket](/appsec/comparisons/dependabot-vs-socket/) ## Sources - [Socket pricing](https://socket.dev/pricing) - [Socket billing and seat minimums](https://docs.socket.dev/docs/billing) - [Socket repository setup](https://docs.socket.dev/docs/repositories-1) Facts and prices were checked against the official vendor pages on July 28, 2026. --- ## Complete Guide to SonarQube for Static Analysis (2026) URL: /appsec/sonarqube/ Cluster: Application Security > Overview & Application Security SonarQube analyzes source code against [quality and security rules](/appsec/), then turns the results into quality gates for branches and pull requests. It comes as managed Cloud, self-hosted Server, and a free Community Build with a narrower workflow. Choose Cloud Team or a commercial Server edition when you need a pre-merge pull request gate, because the free options gate only the main branch. ## At a Glance | Requirement | Starting Point | | Managed private project | Cloud Free | | Managed pull request gate | Cloud Team | | Self-hosted main branch | Community Build | | Self-hosted pull request gate | Server Developer | | Dependency analysis | Advanced Security | | AI fix suggestions | Paid plan | ## What Does SonarQube Analyse? SonarQube performs static analysis, which means a scanner reads source and configuration files without observing the deployed application at runtime. The scanner applies the rules in a language's quality profile, then sends the results to Cloud, Server, or Community Build for storage and quality-gate calculation. Sonar provides scanners for common build systems and languages, including .NET, Maven, Gradle, npm, and Python. A general SonarScanner CLI covers projects that do not use one of the dedicated scanners. Findings are grouped as security, reliability, and maintainability issues, with security hotspots kept for patterns that need human review. A hotspot asks a reviewer to decide whether the code is safe in its context. Treating every hotspot as a confirmed vulnerability would inflate the queue and hide the distinction Sonar intends. Core security includes taint analysis, which follows untrusted data from its source to operations where it could cause a security flaw. Sonar also lists infrastructure-as-code scanning and secrets detection in the core product. The exact rules still vary by language and edition. SonarQube does not execute the application as a dynamic scanner would. A static rule can identify an unsafe data flow in code, while runtime authorization mistakes, network controls, and deployment behavior need other tests. Keep that boundary clear when building an application security stack. ## Should You Choose SonarQube Cloud, Server or Community Build? Choose SonarQube Cloud when you want Sonar to operate the service, Server when you need a commercial self-hosted deployment, or Community Build when free main-branch analysis is sufficient. The choice affects pull request support, language coverage and maintenance work as well as hosting. SonarQube Server is the commercial self-hosted product. The customer manages the application, database, upgrades, backups, network access, and scanner connectivity. Developer, Enterprise, and Data Center editions add capacity and features at different line-of-code thresholds. SonarQube Community Build is the free self-hosted option. It provides automated review of the main branch and works as a central baseline when the team can accept that branch limit. | Deployment | Hosting | Entry Limit | | Cloud Free | Sonar | 50K private LOC | | Cloud Team | Sonar | 100K private LOC | | Community Build | Customer | Main branch | | Server Developer | Customer | 100K LOC | The choice changes more than who runs the server. It changes branch analysis, pull request feedback, language coverage, identity controls, and the work required to keep the analysis service available. Include those operational costs when comparing Server with Cloud. ## How Do SonarQube Quality Profiles and Gates Work? A SonarQube quality profile chooses the rules for a language; a quality gate checks the resulting measures and returns Passed or Failed. Configure CI or repository rules to require that status when a failed gate should block a merge. The gate becomes useful when the CI or repository ruleset treats its status as a merge condition. A failure can come from a new reliability issue, a security finding, an unreviewed hotspot, coverage, or duplication, depending on the configured conditions. The gate itself does not merge or fix code. Sonar's new-code model separates findings introduced by current work from debt that already exists. A team can enforce strict rules on changed code while keeping an owned backlog for older findings. That makes adoption possible without granting permanent exceptions to every old issue. Start with the built-in gate on one representative project. Record which findings block a pull request, who can mark a hotspot reviewed, and how an exception expires. If nobody fixes the findings that fail a gate, developers may start bypassing it. Custom quality gates and profiles depend on the plan. SonarQube Cloud provides them on Team and Enterprise, while Cloud Free applies the built-in Sonar standards. ## How Does SonarQube Analyse Pull Requests? SonarQube Cloud pull request analysis compares the pull request head with the target branch and reports issues introduced by that change. An up-to-date target-branch analysis is required for an accurate comparison. On eligible plans, the result appears in SonarQube and can decorate the pull request in GitHub, GitLab, Azure DevOps, or Bitbucket Cloud. Depending on the repository platform, findings may also appear as inline annotations. Repository rules can block the merge when the quality gate fails. Cloud Free analyzes the main branch and exposes pull request analysis only after the change has merged into `main`. It cannot supply a pre-merge pull request gate. Cloud Team and Enterprise provide pre-merge analysis, while the OSS plan provides unlimited branch and pull request analysis for eligible open-source organizations. Community Build confines automated analysis to the repository's main branch. Commercial Server editions add branch and pull request workflows, with Developer as the entry edition shown on the current pricing page. A team that needs pre-merge enforcement should include that edition boundary in the Server cost. Pull request analysis focuses on new code. Main-branch analysis still matters because it can apply broader rules and keeps the comparison base current. Schedule both paths instead of assuming an inline comment is the whole result. ## What Does SonarQube Advanced Security Add? SonarQube Advanced Security adds dependency analysis to core source-code checks, including known vulnerabilities, malicious packages, licence policies and software bills of materials. Its availability and subscription terms differ between Cloud and Server, so confirm the entitlement for your deployment. Advanced Security adds software composition analysis and advanced static analysis across dependency boundaries. Sonar documents dependency vulnerabilities, malicious packages, license policy, software bill of materials exports, and dependency-aware data flow among the add-on's capabilities. Sonar's Cloud documentation says the product is available starting with the Team plan and requires a separate subscription that neither Team nor Enterprise includes. Sonar's pricing page also disagrees with itself. The Team card lists Advanced Security among its inclusions, while the same page's FAQ says it is available to Enterprise plan users. Get the exact Cloud entitlement and price in writing before treating any of these lines as the contract. For self-hosted Server, Advanced Security is an add-on starting with Enterprise. SCA must be enabled, and the built project must expose supported dependency files for analysis. Sonar's SCA is a documented dependency scanner, but the team still needs to test the supported package managers and malicious-package policy against its own stack. Keep [Dependabot alerts](/appsec/dependabot/) or another advisory source active during the evaluation so one product trial does not create a gap in the existing queue. ## How Does SonarQube Check and Fix AI-Generated Code? SonarQube AI Code Assurance applies a quality standard to projects marked as containing AI-generated code. AI CodeFix separately suggests edits for eligible findings. Neither feature proves who wrote every line or removes the need to review and test a change. The control analyzes code with Sonar rules after the project is labeled. It does not prove authorship for each line or replace review of generated changes. Automatic GitHub Copilot detection is deprecated in the current Cloud and Server documentation, so a lasting workflow should use explicit project policy rather than depend on that signal. AI CodeFix generates a proposed edit for eligible findings. Cloud Team and Enterprise provide it for selected rules and languages. The developer can inspect the diff in SonarQube or a connected IDE before applying it. On self-hosted Server, AI CodeFix requires Enterprise or Data Center. When Sonar's hosted model path is used, an affected code snippet and issue description are sent to the chosen model service. Review data-handling terms and language eligibility before enabling the feature on sensitive repositories. Every suggested fix still needs a focused test before it is accepted. Reproduce the reported issue, apply the smallest eligible edit, run the security regression, and rescan with SonarQube. Check that the finding is resolved and the application still behaves as intended. ## What Are SonarQube’s Limits? SonarQube’s static analysis does not observe the running application. Runtime authorisation, deployment identity, network policy and attacks involving live data need other tests or controls. Language support, branch analysis and advanced features also depend on the edition. Security hotspots require human judgment by design. Assign reviewers who understand the framework and threat behind each hotspot category. Closing them in bulk to make a gate green removes the review that the feature is meant to require. Legacy findings can bury a new rollout. Sonar's new-code model gives teams a way to enforce current changes while they triage the backlog. Set an owner and service level for old high-severity findings, then expand the gate after measuring how much review work the initial scan creates. Language counts and analyzer coverage differ between SonarQube plans. The current Cloud pricing page advertises more than 30 languages on Team and more than 40 on Enterprise. Server Developer and Enterprise also have different language sets. Check the exact language and rule set instead of buying from the headline count. Advanced Security has its own rollout cost. Dependency analysis can create a large first backlog, and Sonar recommends starting with one willing team before adding dependency-risk gate conditions. ## How Much Does SonarQube Cost? SonarQube Cloud offers a free tier for up to 50,000 private lines of code, with Team starting at $34 per month for 100,000 private lines. Commercial Server pricing is quoted per instance and year by line count. Community Build is free, but you pay for hosting and maintenance. ### SonarQube Cloud Cloud Team starts at $34 per month for up to 100,000 private lines of code. Free covers up to 50,000 private lines, while Enterprise uses custom annual pricing and a wider feature set. | Plan | Starting Price | Plan Scope | | Free | $0 | 50K LOC; post-merge PRs | | Team | $34/month | 100K+ LOC; PR gates | | Enterprise | Custom | Unlimited option; enterprise controls | | OSS | $0 | Eligible public projects | Team includes secrets detection, pull request analysis, AI CodeFix, and the current Team language set. Enterprise adds organization hierarchy, SSO and SCIM, broader languages, portfolio controls, security reports, and other governance features listed on the plan page. ### SonarQube Server Sonar publishes no list price for any Server edition. Developer offers a free trial, while Enterprise and Data Center route to sales. Each edition is priced per instance, per year, by lines of code. The license covers a maximum line count for one installation. | Edition | Annual Price | Edition Scope | | Community Build | $0 | Core set; main branch | | Developer | Quote | Branch and pull request analysis | | Enterprise | Custom | Broader languages; AI CodeFix | | Data Center | Custom | High availability | Server cost includes the license plus the infrastructure and staff time for upgrades, backups, monitoring, database care, and incident response. Compare that full annual figure with Cloud instead of treating self-hosting as free after the license is paid. ## How Should You Roll Out SonarQube? Roll out SonarQube on one representative repository, review the first findings and then require a narrow quality gate for new code. Assign engineering and security owners so valid findings get fixed and false positives are handled consistently. 1. Choose the deployment. Decide whether code and analysis data may leave the network before comparing features. 2. Verify the language. Check the exact analyzer, framework rules, and edition on the current support page. 3. Scan the main branch. Classify true findings, false positives, hotspots, and existing debt before enabling a gate. 4. Set the new-code baseline. Keep current pull requests strict while assigning the old backlog to a separate plan. 5. Protect one pull request. Confirm decoration, inline findings, status checks, and the exception path on a test branch. 6. Test an eligible fix. Apply an AI CodeFix only after adding a regression test for the reported issue. 7. Price the final scope. Count private lines, Server operations, Advanced Security, and commercial help in one annual figure. Keep the first gate narrow enough that developers trust it. Expand rule coverage after the team can explain each blocked merge and resolve it without bypassing the check. ## The Verdict Choose SonarQube when static analysis needs a documented quality gate across supported languages and the organization wants either SonarQube Cloud or customer-managed Server deployment. Cloud Free and Community Build provide main-branch baselines. Pre-merge pull request analysis requires Cloud Team, Cloud Enterprise, an eligible OSS plan, or a qualifying Server edition. Choose the edition that includes the features you need; the SonarQube name alone does not tell you what is included. Confirm language rules, branch coverage, AI CodeFix data handling, and Advanced Security entitlement against a representative repository. Choose SonarQube for first-party code and quality governance, then keep separate dependency and runtime controls where the selected plan does not cover them. ## Frequently Asked Questions ### Does SonarQube Cloud Free analyze pull requests? SonarQube Cloud Free exposes pull request analysis only after the pull request has merged into main, so it cannot protect a merge with a pre-merge gate. Cloud Team and Enterprise provide pre-merge pull request analysis. The separate OSS plan provides it for eligible open-source organizations. ### Does SonarQube Community Build include pull request analysis? SonarQube Community Build focuses on automated analysis of the main branch. Its comparison table places multi-branch and pull request analysis in other deployments. Use Community Build for a self-hosted main-branch baseline, then price Server Developer when pre-merge branch analysis is required. ### Which plans include SonarQube AI CodeFix? Sonar documents AI CodeFix on Cloud Team and Enterprise. On self-hosted Server, it requires Enterprise or Data Center. The feature covers selected rules and languages, sends an affected code snippet to the chosen model service, and applies monthly usage limits when Sonar hosts the model path. ### Does SonarQube include software composition analysis? Software composition analysis comes through SonarQube Advanced Security, a separate subscription. Current Cloud documentation makes it available starting with the Team plan, while the FAQ on Sonar's pricing page ties it to Enterprise plan users. Server documentation makes it an add-on starting with Enterprise. Confirm the exact Cloud entitlement and price in writing. ### Can SonarQube replace Dependabot alerts? Core SonarQube does not replace Dependabot’s dependency advisories and security-update pull requests. SonarQube Advanced Security adds dependency analysis, but its plan requirements and update workflow differ. Keep Dependabot active until you have confirmed how every required check and update will be handled. ## Where to Go Next - [Application Security hub](/appsec/) - [Snyk guide](/appsec/snyk/) - [Socket guide](/appsec/socket/) - [Dependabot guide](/appsec/dependabot/) - [Aikido vs SonarQube](/appsec/comparisons/aikido-vs-sonarqube/) ## Sources - [SonarQube Cloud pricing](https://www.sonarsource.com/plans-and-pricing/) - [SonarQube Server editions](https://www.sonarsource.com/plans-and-pricing/sonarqube/) - [SonarQube Cloud documentation](https://docs.sonarsource.com/sonarqube-cloud/) Updated September 24, 2026: Sonar no longer publishes a price for SonarQube Server Developer, and Sonar's Cloud documentation now places Advanced Security from the Team plan as a separate subscription. --- ## Aikido vs SonarQube: All-in-One AppSec vs Deep SAST URL: /appsec/comparisons/aikido-vs-sonarqube/ Cluster: Application Security > Comparisons Aikido and SonarQube meet most clearly around static analysis, pull request feedback, and security policy for code. The choice depends on what else you need to scan and where you need the service to run. Aikido sells a broad security platform that reaches code, packages, cloud assets, domains, and runtime controls. SonarQube centers code verification through Cloud, Server, and IDE products. That overlap has to be examined at the plan level. SonarQube Advanced Security now adds software composition analysis and package-risk features, while Aikido has expanded editor and code-quality workflows alongside its broader scanners. ## Aikido and SonarQube at a Glance | Attribute | Aikido | SonarQube | | Core shape | Broad AppSec platform | Code verification platform | | Static scan | SAST and AI SAST | SAST and taint analysis | | Dependency scan | Included SCA | Advanced Security add-on | | Dynamic test | Domains and APIs | Code-focused product | | Self-managed | Local scan controls | SonarQube Server | | Cloud billing | Plan and users | Private code lines | | Policy center | Security feed and gates | Profiles and gates | ## Should You Choose Aikido or SonarQube? Choose SonarQube for code quality rules, quality gates and a self-managed server. Choose Aikido when you want code, dependency, cloud and running-application checks in one platform. Both scan source code, but they do not cover the same jobs. - Must the platform govern new source code, existing code debt, third-party packages, or every one of those surfaces? - Does the organization require a fully self-managed server, a local scanner, or a cloud service with controlled connections? - Are domain scanning, API testing, cloud posture, container images, or runtime controls part of the same purchase? - Will the enforced policy live in an editor, a pull request check, a release job, or a central quality gate? - Does cost grow faster with private lines of code, protected assets, or engineering users? Both are worth testing for static analysis with enforced rules. If you also need dynamic testing, cloud checks, or a self-managed code-analysis server, compare those requirements separately. ## How Do Aikido and SonarQube Check Your Code? Aikido and SonarQube both scan source code for security flaws. SonarQube also puts reliability and maintainability rules at the centre of its code quality workflow; Aikido combines code scanning with checks on dependencies, cloud services and running applications. SonarQube analyzes source against quality profiles covering reliability, maintainability, and security rules. Its official security-rule reference describes taint analysis for injection paths, plus security hotspots that require human review. Aikido's static analysis runs in connected cloud scans, pull request checks, release gates, and supported editors. Its AutoTriage documentation says triage follows the scan and uses exploitability and severity context to adjust findings. Comparative accuracy on a private codebase requires safe fixtures that reflect the application's frameworks and trust boundaries. Include an expected true positive, a safe lookalike, and a data-flow case crossing more than one file. ### Quality Gates and New Code SonarQube's quality gate checks whether a change meets the conditions your team sets. Conditions can apply to new code or overall code, and a pull request receives the gate result in the connected source-control platform. Sonar's quality-gate documentation defines those conditions. The new-code model lets a team enforce current standards while handling existing debt through a separate plan. Test how branch settings, exclusions, and gate conditions are administered, because a permissive project override can weaken a central policy. Aikido documents pull request and release gates across multiple scanner types. Its gating overview separates native pull request integrations from command-line release checks. That broad gate can reduce separate jobs when the enabled scanners fit the program. ### Editor and Pull Request Feedback SonarQube for IDE provides local analysis and can connect to SonarQube Cloud or Server so the editor uses the central profiles and settings. Pull request analysis reports issues introduced by the pull request and can publish the gate status back to the development platform. The branch-analysis documentation defines the available analysis modes. Aikido lists plugins for VS Code-family editors, JetBrains products, Visual Studio, and Eclipse. Its IDE comparison says fast local scans have less repository and deployment context than cloud scans. Generated fixes need the same controls as any code patch. Aikido documents AutoFix for supported static and dependency findings. SonarQube documents AI CodeFix for a selected set of rules and languages, with plan or edition limits. Review the diff, run the tests, and keep branch protection in force. ## Do Aikido and SonarQube Scan Dependencies? Aikido and SonarQube both offer dependency scanning, but access differs. Aikido includes SCA in its platform plans; SonarQube requires eligible Advanced Security access. Aikido includes software composition analysis in its platform plans. Its dependency reference lists supported manifests and lockfiles across JavaScript, Python, Java, Go, Rust, .NET, Ruby, and other ecosystems. SonarQube Advanced Security adds software composition analysis and dependency-aware static analysis to SonarQube's core code checks. Sonar's official product page describes known-vulnerability checks, malicious-package detection, license policy, and software bills of materials. The Sonar add-on requirement matters in both trial and price. A base SonarQube scan and an Advanced Security scan cover different package questions. Confirm the license, supported ecosystem, direct and transitive path, and enforcement point in writing before comparing its result with Aikido SCA. Neither product should inherit claims from a separate supply-chain tool. If the requirement includes local package scoring or an install-time firewall, compare the documented [Dependabot and Socket workflow](/appsec/comparisons/dependabot-vs-socket/) as a distinct control layer. ## Web, Cloud, and Runtime Coverage Aikido's public plan table includes domain scanning, API scanning, cloud posture management, container and virtual-machine scanning, bot protection, and runtime modules. Availability and asset allowances vary between Developer, Basic, Pro, Advanced, and Enterprise. Its domain-scanning documentation says the scanner probes public-facing components with safe, non-destructive automated tests. SonarQube's current plan table centers source, secrets, infrastructure-as-code, and open-source analysis through Advanced Security. Teams that need active web testing, cloud asset inventory, or runtime controls in the same contract should price those requirements explicitly. SonarQube's security label does not establish those capabilities. Each additional scanner also broadens the required access. A domain scanner needs network reach, a cloud scanner needs cloud permissions, and a runtime control enters the request path. Apply least privilege separately to each connected Aikido module. ## Can Aikido and SonarQube Run on Your Own Infrastructure? SonarQube Server is the fully self-managed option in this comparison. Sonar describes Developer, Enterprise, and Data Center editions priced per instance and private lines of code. Server places the service and analysis results inside infrastructure controlled by the customer, subject to the connected integrations and any enabled external AI service. SonarQube Cloud provides the vendor-hosted analysis path for teams. Its public plan page lists a free allowance for private code and paid Team analysis from 100,000 private lines. The largest private branch determines a project's billed line count under the current pricing FAQ. Aikido connects to hosted and self-managed source-control systems and documents a local scanner account for teams that want code to stay on their premises. Its current pricing table places on-prem scanning in Pro and an internal-app broker in Advanced. A local scanner is a different architecture from a self-managed platform. Map what remains in the network, what metadata reaches the vendor, where findings are stored, which AI features call external services, and how the connection is revoked. ## How Much Do Aikido and SonarQube Cost? The public figures below are a planning baseline. They are useful for a first model and insufficient for a purchase order because Advanced Security, enterprise controls, annual discounts, and protected-asset needs can change the total. | Plan point | Aikido | SonarQube | | Free | Two users | 50K private lines | | Cloud entry | $300 monthly | $34 monthly | | Higher entry | $600 monthly | Custom Enterprise | | Billing base | Plans and assets | Private code lines | | Self-managed | Eligible scan controls | Annual Server license | Aikido lists Basic at $300 monthly and Pro at $600 monthly with ten included users in its public calculator. Repository, container, domain, cloud-account, virtual-machine, protected request, and AutoFix allowances vary by tier on the official pricing page. SonarQube Cloud lists Team from $34 monthly for up to 100,000 private lines of code. SonarQube Server uses annual per-instance licenses based on lines of code across Developer, Enterprise, and Data Center editions. Advanced Security is separately priced for eligible plans. Add infrastructure, upgrades, backups, availability, and administrator time to a Server model. Add the cost of protected assets and extra scanner ownership to an Aikido model. A smaller subscription can become the more expensive program when its operating work is ignored. ## How to Run a Matched Trial 1. Select one representative repository with the languages, frameworks, branch rules, and dependency manager used in production. 2. Create safe fixtures for static data flow, a secret, an infrastructure error, and a dependency issue each vendor documents. 3. Run local or editor checks, pull request analysis, the central gate, and the default-branch scan. 4. Record finding evidence, false positives, missed fixtures, scan time, suppression controls, and fix effort. 5. Test the least-privileged source-control installation and every policy bypass available to a repository administrator. 6. Price the exact scanner set and deployment architecture that passed the technical trial. Score Aikido’s dynamic, cloud, and runtime tests separately when SonarQube does not offer the equivalent check. That prevents wider scope from distorting the static-analysis result and makes the extra Aikido surfaces visible in the budget. ### Evidence to Keep Save the fixture commit, scanner configuration, plan, scan time, and raw result for each matched case. Record the rule identifier, data path, severity, ownership route, suppression options, and pull request status. This makes the trial reviewable after a dashboard or default rule changes. Save the pricing page, quoted plan, billing unit, asset count, and enabled add-ons with the same evidence. At renewal, you can use that record to repeat the tests and recalculate the cost. Keep the deployment and permission evidence with every trial result. For SonarQube Server, include the edition, configured quality profile, gate, plugins, and external services. For Aikido, include every enabled scanner, connected asset, broker or local scanner, and permission scope. Without those settings, a later reviewer cannot tell whether the result applies to the setup you plan to buy. Finish the evaluation with a full integration-removal test. Disconnect the trial repository, revoke credentials, remove a user, and document the retention path for findings and source-derived data. Security tooling receives privileged access, so clean offboarding belongs in the evaluation alongside scan quality and developer speed. Have an engineer and a security reviewer score the same evidence independently. Differences expose unclear rules, weak remediation context, and policy assumptions before they become a permanent source of triage work. ## Read Next - [Aikido guide](/appsec/aikido/) maps the scanners, plan boundaries, integrations, and deployment controls. - [SonarQube guide](/appsec/sonarqube/) covers Cloud, Server, quality gates, supported code, and Advanced Security. - [Snyk vs Aikido](/appsec/comparisons/snyk-vs-aikido/) compares two cloud-first platforms with more direct overlap in developer workflow. - [Dependabot vs Socket](/appsec/comparisons/dependabot-vs-socket/) focuses on dependency advisories, package behavior, and install controls. - [Application Security comparisons](/appsec/comparisons/) places the full tool set on the same threat surfaces. ## Security Verdict Choose SonarQube when the program is centered on governed code verification, quality gates, and a customer-managed Server deployment. Choose Aikido when the same purchase must cover static code, dependencies, domains, APIs, cloud assets, containers, and selected runtime controls. A team that needs both should keep SonarQube as the code gate and buy Aikido only where the trial shows it can replace existing tools. ## Frequently Asked Questions ### Does SonarQube scan open-source dependencies? SonarQube Advanced Security includes software composition analysis for supported ecosystems. Sonar documents vulnerability, license, software-bill-of-materials, and malicious-package features. Advanced Security is an add-on tied to eligible paid plans, so base SonarQube access should not be treated as equivalent dependency coverage. ### Does Aikido have quality gates for pull requests? Aikido documents pull request gating through native source-control integrations and release gating through its command line interface. Its pull request gate can check software composition, infrastructure files, secrets, static code, malware, licenses, and code quality. Exact checks depend on the connected project and plan. ### Can Aikido and SonarQube run analysis inside a private network? SonarQube Server is a self-managed product installed in the customer's infrastructure. Aikido documents local scanning and internal-app brokers on eligible plans while retaining its service around those controls. Check what source code and scan results each enabled feature sends outside your network. ### How do SonarQube and Aikido count usage? SonarQube Cloud prices private analysis by lines of code, and SonarQube Server editions use annual per-instance licensing based on lines of code. Aikido's public plans combine a base fee, included users, and allowances for repositories and other protected assets. Enterprise quotes can introduce additional commercial terms. ### Do Aikido and SonarQube generate AI fixes? Aikido and SonarQube offer generated fixes for supported findings. Aikido describes AutoFix for selected static and dependency results, while SonarQube documents AI CodeFix for selected rules and languages with plan and edition requirements. Every generated patch still needs review, tests, and the normal merge policy. Facts and prices were checked against the official vendor pages on July 28, 2026. For the wider set of options, see our [application security tools guide](/appsec/). --- ## Dependabot vs Socket: Dependency Scanning and Supply Chain Defense URL: /appsec/comparisons/dependabot-vs-socket/ Cluster: Application Security > Comparisons Dependabot and Socket protect dependency changes at different points in their life cycle. Dependabot connects a GitHub repository to known vulnerability and malware advisories, then can open update pull requests. Socket evaluates package behavior, metadata, dependency trees, and changes before installation or merge. I use Dependabot for known advisories and update pull requests, and Socket for package evidence before adoption and merge. ## Dependabot and Socket at a Glance | Reader Question | Dependabot | Socket | | Known vulnerable version? | Advisory match | Vulnerability signal | | Suspicious package behavior? | Not assessed | Behavior signals | | Stops a malicious install? | No | Firewall | | Opens a fix PR? | Security update | Socket Fix in CI | | Keeps versions current? | Scheduled updates | Socket Fix in CI | | Works outside GitHub? | No | CLI and integrations | ## How Do Dependabot and Socket Differ? Dependabot checks dependencies against published advisories and opens update pull requests. Socket also examines package behaviour, which can reveal suspicious changes before an advisory exists. Published advisories answer one question: does the resolved dependency graph contain a version associated with a known vulnerability or reviewed malware report? Dependabot alerts turn that database match into repository context, including the affected manifest, dependency path, severity, and available patched version. A proposed package also needs scrutiny before an advisory exists. Install scripts, ownership changes, native code, network access, packed files, and a surprising dependency tree can all change the adoption decision. Socket documents these as supply-chain signals that require contextual review. Dependabot helps you respond to a published advisory. Socket adds checks on package behaviour before you adopt a dependency or accept an update. Socket Firewall can block an installation while you investigate a suspicious package. ## What Does Dependabot Check? Dependabot is part of GitHub's repository security surface. The dependency graph resolves package relationships, alerts connect those relationships to the GitHub Advisory Database, and update features propose changes through normal pull requests. That location makes the finding visible beside code ownership, review, and branch protection. ### Alerts and Security Updates A Dependabot alert identifies an affected dependency and displays the advisory, dependency path, vulnerable range, and patched version when one exists. Dependabot security updates can then open a pull request that raises the dependency to the minimum secure version available under the repository's configuration. GitHub now also documents repository-level malware alerts for eight ecosystems, including npm, pip, and Maven. When enabled, they notify maintainers when a dependency matches a reviewed malware advisory in the GitHub Advisory Database. GitHub says detection can take time because reports are reviewed before publication, so the control belongs to the advisory timeline. ### Version Updates Dependabot version updates inspect configured package ecosystems on a schedule and open pull requests for newer releases. The repository's `.github/dependabot.yml` controls ecosystem, directory, cadence, groups, limits, and other update behavior. Alerts are enabled separately in repository or organization security settings. Update grouping deserves deliberate and repository-specific configuration. A broad group means fewer pull requests, but more changes to review or undo at once. Smaller groups take more review time, but make it easier to identify which package changed the build or runtime behaviour. [Socket Fix](https://docs.socket.dev/docs/socket-fix) can also open dependency-fix pull requests when configured in CI with the required tokens. Review and test those updates just as you would a Dependabot update. ## What Does Socket Check? Socket exposes package intelligence through its website, command line interface, source control integrations, CI reports, API, and firewall. The product documents signals such as install scripts, network and file-system access, native code, obfuscation, package ownership, release timing, and dependency changes. A signal is evidence for a decision. A network-capable SDK may need network access, and a compiler may need native binaries. The reviewer still needs to check whether the package needs that behaviour and what it could access in the application. ### Package Score The Socket package command provides a shallow score for the selected package and a deep score that includes transitive dependencies. It also exposes issue details that can be inspected before changing a manifest. I run `socket package score npm @` before proposing a dependency addition or upgrade. A clean direct score is only half the evidence. I inspect the deep score and require zero critical or high alerts across the package and its transitive tree under the standing repository policy. ### Pull Request Scanning Socket's source-control and CI surfaces evaluate dependency changes as a diff. The Socket CI documentation describes `socket ci` as an alias for creating a scan with a report. Policy failure returns a nonzero exit code for the CI job. In the repositories where I use it, `pnpm exec socket ci` is a pull-request gate when manifests or lockfiles change. It records evidence on the actual resolved diff, after the local candidate check and before merge. An override requires a specific signal review and justification. ### Install-Time Protection Socket Firewall operates before a supported package manager downloads content. Its official overview says it inspects package-manager traffic before download. Socket states that confirmed malicious packages are blocked and packages suspected by its AI receive warnings. The current free Firewall documentation lists npm, Yarn, pnpm, pip, uv, and Cargo. That support list is narrower than every ecosystem Socket can analyze through other surfaces. Verify the package manager itself instead of inferring firewall coverage from a package-score result. ## The Standing Package Preflight This is the workflow I use across repositories. It combines the two products with registry evidence and normal code review. The 24-hour wait and severity threshold are our operating policy; they are intentionally stricter than a vendor's default configuration. 1. Find the current stable candidate from the ecosystem registry and official release source. 2. Confirm that the exact release is more than 24 hours old unless an urgent security fix justifies a documented exception. 3. Run `socket package score` for the exact package and version before editing a manifest. 4. Inspect shallow and deep results, requiring zero critical or high alerts in the package and transitive tree. 5. Compare the candidate with the existing version and investigate a material score regression or new capability. 6. Cross-check GitHub advisories, repository Dependabot alerts, and available maintainer guidance. 7. Make the smallest exact dependency change using the repository's pinned package manager with lifecycle scripts disabled. 8. Review the lockfile diff, run the product tests, and require the Socket pull-request scan to pass. The sequence matters because each step answers a different question. Release age provides a short observation window, package scoring exposes behavioral and dependency-tree signals, and advisory databases cover published, known reports. Tests check compatibility in this codebase, while the pull-request scan checks the final resolved dependency change. An exception keeps the same evidence trail. For an actively exploited vulnerability, waiting may increase risk. Record why the newer secure release is urgent, inspect its package and transitive signals, constrain the diff, run the full verification, and keep rollback simple. ## What If Dependabot and Socket Disagree? A clean Dependabot result does not overrule a Socket warning, and a clean Socket result does not clear a known vulnerability. Check the reason for each alert before accepting the package. A green Dependabot state says the resolved version has no matching alert under the enabled advisory data. A Socket issue can still point to a package behavior, ownership event, or dependency change that needs review. Preserve both records because the evidence answers separate questions. An ordinary Socket result can accompany a Dependabot vulnerability alert. Follow the vulnerable dependency path, confirm the affected range, inspect the patched release, and test the smallest secure update. A package score never clears a known advisory. Treat tool failure as a visible state. An unavailable scan, missing lockfile, unsupported ecosystem, shallow clone, or expired integration can leave you with no findings because the check did not work. Required checks should fail closed for dependency changes, with an emergency path that records approver, reason, compensating review, and follow-up. ## Triage and Policy Ownership Dependabot needs an owner for alert review, update grouping, ignored versions, and stale pull requests. Socket needs an owner for organization policy, new package signals, baselines, and overrides. Leaving either queue unattended turns a security control into background noise. Route advisory and package-signal events to different owners. A known critical advisory can enter the incident or urgent-fix path with the affected services attached. A new install-script signal belongs with the dependency-change reviewer, who can compare the script to the package's documented build and release model. Auto-triage rules can reduce repetition, but the rule needs a narrow reason and an expiry condition. GitHub's auto-triage documentation describes presets and custom rules. Confirm plan availability before designing a process around custom behavior. ## Three Routine Dependency Cases | Case | Start | Required Proof | | Routine update | Exact-version preflight | Tests and PR report | | Security alert | Vulnerable dependency path | Patched version and tests | | New dependency | Need and alternatives | Full package review | For a routine update, check release age, score the exact version, inspect the transitive tree, and read the changelog before changing the manifest. Tests and the Socket pull request report then validate the actual lockfile diff. A security alert starts from the advisory and vulnerable path. Score the patched candidate, review unrelated lockfile movement, and run the product tests. The alert supplies urgency, but it does not prove compatibility or low package risk. A new dependency starts with whether the product needs the package. When it is justified, inspect maintenance, permissions, package signals, transitive size, license, release age, and advisories before any install. These cases should leave the same evidence: exact candidate, reason for change, package and advisory research, manifest and lockfile diff, tests, policy result, reviewer, and exception if one was needed. Consistent evidence makes later incident response and dependency removal much faster. ## Ecosystems and Platform Fit Dependabot is native to GitHub and supports the ecosystems listed in GitHub's current support matrix. Support varies between dependency graphs, alerts, security updates, version updates and vendored dependencies. Socket's package intelligence spans several ecosystems, while individual products have their own availability. Check the CLI command, source-control integration, Firewall package manager, and plan separately. A successful npm workflow should never be presented as proof of identical Maven, NuGet, Go, Python, or Ruby behavior. Repository location is decisive because Dependabot operates inside GitHub. Socket offers its command line tools independently and lists additional source-control platforms on higher plans. Teams outside GitHub still need a separate version-update mechanism and must verify integration support in the contract. ## How Much Do Dependabot and Socket Cost? GitHub documents Dependabot alerts and the core update features as available without a GitHub Code Security license. Some adjacent features, including certain advanced security controls, carry separate plan requirements. Confirm the repository visibility and organization plan in GitHub's feature table. | Plan point | Dependabot | Socket | | Core entry | GitHub feature | Free plan | | Free allowance | Alerts and updates | 1,000 scans monthly | | Team | GitHub plan context | $25 per developer monthly | | Business | Adjacent licenses vary | $50 per developer monthly | | Enterprise | GitHub contract | Custom contract | Socket's pricing page lists Free at $0 with 1,000 monthly scans, Team at $25 per developer monthly with 5,000 scans, and Business at $50 per developer monthly. Limits and included controls vary by plan. Include the time people spend managing these tools in the cost. Count alert triage, pull-request review, false-positive investigation, policy administration, and exception records. A free queue that nobody owns has little operational value, while an overly broad paid policy can stop routine changes without improving a decision. ## How to Test the Pair 1. Enable the dependency graph and Dependabot alerts on a non-critical repository with an assigned owner. 2. Configure one conservative version-update schedule and a small, coherent dependency group. 3. Score the current package set and one proposed upgrade with Socket, recording shallow and deep evidence. 4. Open a test pull request that changes a manifest and lockfile, then inspect both tools' repository output. 5. Use a safe internal fixture to exercise an approved policy signal; never install known malware for a product trial. 6. Measure review time, unexplained findings, update success, and the clarity of the audit record. Keep every package trial safe, isolated, and reproducible. A package with a benign, documented install script can test whether reviewers understand a signal without importing malicious code. A deliberately stale dependency in an isolated fixture can test advisory and update handling. ## Read Next - [Dependabot guide](/appsec/dependabot/) for repository setup, alerts, update configuration, and triage. - [Socket guide](/appsec/socket/) for package intelligence, CI scans, and install-time controls. - [Application Security comparisons](/appsec/comparisons/) for code, package, cloud, and runtime tool boundaries. - [Application Security hub](/appsec/) for the wider control model. ## Supply-Chain Verdict Run Dependabot and Socket together for a GitHub supply-chain workflow. Dependabot owns known advisory response and routine version-update pull requests. Socket owns pre-adoption package evidence, dependency-diff policy, and supported install-time controls. The practical starting point is Dependabot alerts with a named triage owner, followed by the Socket package preflight and pull-request scan for every dependency change. That combination preserves two distinct forms of evidence without pretending either product can make the final risk decision for the team. ## Frequently Asked Questions ### Do Dependabot malware alerts work like Socket Firewall? Dependabot malware alerts and Socket Firewall check dependencies at different stages. Dependabot malware alerts notify a repository when a dependency in a supported ecosystem matches a reviewed malware advisory in the GitHub Advisory Database. Socket Firewall checks supported package-manager traffic before a package reaches the machine and blocks packages Socket has confirmed as malicious. ### Does Socket package score include transitive dependencies? Socket documents a shallow score for the package itself and a deep score that includes its dependency tree. A package preflight should inspect both views because clean direct-package results can sit above a risky transitive dependency. ### Does dependabot.yml enable Dependabot alerts? Dependabot alerts are enabled through repository or organization security settings. The dependabot.yml file configures version updates and can also customize update behavior. A repository can therefore have alerts without scheduled version-update pull requests. ### Can Socket CI replace a local package preflight? Socket CI and a local package preflight check dependencies at different stages. A local package score informs the choice before a manifest changes. Socket CI evaluates the resulting dependency diff in the pull request and records a shared policy result before merge. ### Why wait 24 hours before installing a new package version? The conservative 24-hour age rule comes from team policy, and Socket and Dependabot provide no guarantee for that window. It gives registries, maintainers, scanners, and other users some time to surface a bad release before the team adopts it. High-severity fixes can use an explicit exception with the same evidence and review. Updated September 24, 2026: Dependabot malware alerts now cover eight ecosystems instead of npm alone. --- ## Snyk vs Aikido: Developer-First AppSec vs Modern All-in-One URL: /appsec/comparisons/snyk-vs-aikido/ Cluster: Application Security > Comparisons Snyk and Aikido both sell application security platforms to development teams, but they package their scanners differently. Snyk divides scanning into named products and places much of the feedback in developer tools. Aikido bundles a wider set of code, cloud, domain, and runtime checks inside plan tiers. Pick Snyk when developer-tool feedback from a few named scanners is the need, and Aikido when you are consolidating many scanners under one plan. Then compare the findings on your languages, where developers see them, how each service handles data, and how the bill changes as your team grows. ## Snyk and Aikido at a Glance | Attribute | Snyk Product and Plan | Aikido Product and Plan | | Core shape | Named scanner products | Bundled plan tiers | | Dependency scan | Snyk Open Source | Included SCA | | Source scan | Snyk Code | SAST and AI SAST | | Dynamic test | API & Web | Aikido DAST | | IDE path | Four plugin families | Aikido plugin range | | Entry billing | Per contributor | Plan plus users | | Private access | Enterprise Broker | Aikido local controls | ## Should You Choose Snyk or Aikido? Choose Snyk when you want to adopt its scanners around your existing development tools. Choose Aikido when you want to manage code, dependency, cloud and running-application checks through one bundled platform. Check the languages and scan limits of the plan you would actually buy. - How much of the need is dependency analysis, static code analysis, cloud posture, or testing of a running service? - Which findings must appear in the editor, pull request, continuous integration job, or central dashboard? - Can the service receive source, dependency metadata, requests, responses, or repository credentials? - Will contributor growth or the number of protected assets drive the larger bill? The final decision should use evidence from the same repository and test application. Vendor feature names often describe related capabilities with different scope, limits, and enforcement. ## What Do Snyk and Aikido Scan? Snyk and Aikido both scan your source code and open-source dependencies. Their current sites also describe dynamic testing, cloud-related checks, container scanning, and infrastructure-as-code analysis. Compare what each scanner finds, the evidence it provides, and how developers act on it. ### Dependency Scanning Snyk Open Source examines direct and transitive dependencies for known vulnerabilities and license issues. Snyk's current plan comparison lists transitive dependency analysis, base plan test allowances, and product-specific limits. Aikido's software composition analysis examines supported lockfiles and manifests for vulnerabilities and risky licenses. Its language and lockfile reference includes pnpm, npm, Yarn, Bun, Python, Java, Go, Rust, .NET, Ruby, and several other ecosystems. A trial should use a direct vulnerable package, a transitive path, and a private-registry dependency. Record whether each product identifies the path to the vulnerable package, provides a safe version, explains reachability, and preserves the package manager's lockfile rules. ### Static Code Analysis Snyk Code provides static application security testing in source-control checks, the command line, and supported editors. Snyk's IDE documentation lists VS Code, JetBrains, Visual Studio, and Eclipse plugin families, with Cursor and Windsurf (now Devin Desktop) covered through the VS Code extension. Aikido documents static scans in its cloud platform and real-time scans in supported editors. Its cloud-versus-IDE page says editor scans use less repository and deployment context than cloud scans. That is a useful warning for any team treating an inline finding as the full project result. Neither vendor's documentation proves that one scanner will find more relevant flaws in a specific codebase. Use safe fixtures for the frameworks and languages that matter. Count reproducible findings and the time required to validate them, then inspect missed fixtures before scoring either product. ### APIs, Web Apps, and Cloud Assets Snyk API & Web tests running websites and APIs. Its official data-handling documentation describes dynamic application security testing for web applications and APIs, along with stored request, response, credential, and discovery data. Aikido's pricing table lists domain scanning, API scanning, cloud posture checks, container scanning, and runtime modules across its tiers. The same official table shows that allowances and advanced scan types vary between Developer, Basic, Pro, Advanced, and Enterprise. Dynamic scanners send traffic to an application. Run them against a staging target with test data and known ownership. Confirm authentication scope, request rate, exclusions, and retained evidence before pointing either service at a production API. ## How Do Snyk and Aikido Fit Into Development? Snyk and Aikido both provide editor checks, command-line tools and pull request feedback. The useful difference is whether the checks you need work in your editor, language and paid plan. Snyk documents plugins for Visual Studio Code, Visual Studio, and JetBrains IDEs, with the Snyk CLI providing many editor actions. The official plugin page warns that supported editor versions move, and its compatibility matrix changes as stable plugin and CLI releases ship. Check that matrix on rollout day and keep policy documents independent of a fixed editor list. Aikido's current IDE overview lists Visual Studio, VS Code, JetBrains products, Eclipse, Cursor, Windsurf (now Devin Desktop), Kiro, and Google Antigravity. Its IDE feature docs describe inline SAST, secret, infrastructure-as-code, and dependency feedback, with feature support depending on the finding and plan. Pull request enforcement also differs from editor feedback. Aikido's gating documentation separates native pull request gating from release gating through its CLI. Snyk's scan overview describes pull request checks across open-source libraries and first-party code. Test the branch-protection path with a contributor who lacks admin rights. A correct comment does not stop a merge if the contributor can bypass the required check. ## Deployment and Data Controls Snyk describes a cloud-first service with multi-tenant SaaS, single-tenant SaaS, and Snyk Broker deployment options. The data-handling documentation says Broker keeps sensitive credentials behind the firewall while the service receives approved data needed for the selected product. Snyk Code Local Engine is deprecated and scheduled for removal. Existing customers receive contractual support during the transition, so it is not a sound architecture for a new deployment. Aikido's setup documentation offers repository connections through common source-control systems and points to a local scanner account when code must stay on the customer's premises. Its current pricing table places on-prem scanning in Pro and brokers for internal applications in Advanced. These are hybrid controls around vendor services. A strict air-gap rule needs a written data flow for source, manifests, findings, telemetry, AI-generated fixes, and credentials. Ask both vendors to map that flow against the exact plan being quoted. ## How Much Do Snyk and Aikido Cost? Snyk Team starts at $25 a month for up to ten developers. Aikido Basic starts at $300 a month with ten users shown in its calculator. Those prices cover different allowances, so check the projects, tests and protected assets you need before comparing the totals. | Plan point | Snyk | Aikido | | Free | $0 | $0, two users | | Team entry | $25 monthly | $300 monthly | | Higher entry | Contact sales | $600 monthly | | Base unit | Plan and project limits | Plan and users | | Public source | Snyk plan table | Aikido plan table | Snyk’s current [plan page](https://snyk.io/plans/) lists Team from $25 a month for up to ten developers. Check the project and test allowances as well as the scanner features included. The advertised starting price is not a per-developer quote. Aikido lists Basic at $300 monthly and Pro at $600 monthly, each showing ten users in the public calculator. Repository, container, domain, cloud-account, virtual-machine, protected request, and AI-fix allowances vary across plans on the official pricing page. A small team buying one Snyk product has different arithmetic from a larger team replacing several scanners with Aikido. Build two totals: the invoice for the required scans and the staff time for duplicated integrations, triage, renewals, and policy maintenance. ## Policy, Reporting, and Ownership Give each Snyk or Aikido alert a team responsible for checking and fixing it. During the trial, try assigning a finding, dismissing a false alarm and approving an exception. Note any steps that require a separate spreadsheet or repeated messages. Snyk organizes scanning around products and monitored projects. Its scan documentation separates Open Source, Code, Container, Infrastructure as Code, and API & Web entry points. Confirm which product creates each issue, which policy applies, and whether one code change closes related findings across views. Aikido presents several scanner types through one security feed and uses pull request or release gates for enforcement. Its scan-frequency reference says paid plans run scheduled scans daily while the free plan runs them every three days. Repository events and connected modules can follow different paths, so verify the timing for every required control. Ask each vendor to demonstrate the same operating events: - Assign a code finding and a dependency finding to their real owners. - Apply a time-limited exception with an approver and review date. - Trace a pull request result back to the central finding and policy. - Remove a repository or employee and show how access and retained data change. - Export evidence suitable for the organization's audit and incident records. Do not judge the scanners by finding counts alone. Two scanners can count the same root cause differently, and severity labels can hide distinct exploitability assumptions. Compare a small set of manually reviewed findings, time the ownership handoff, and inspect the evidence behind every suppression. That trial result is more useful than a larger headline finding count. ## How to Trial Both Products 1. Use one representative private repository and a separate staging application with disposable test data. 2. List the exact package, code, infrastructure, secret, container, and dynamic-test fixtures the selected plans document. 3. Run editor, pull request, default-branch, and scheduled scans where each vendor supports them. 4. Measure setup time, actionable findings, repeated findings, missed fixtures, and remediation effort. 5. Test a policy failure from an ordinary contributor account and record every bypass path. 6. Export findings, remove the integrations, revoke credentials, and verify the vendor's documented deletion path. Score each type of scan separately. One strong dependency result cannot compensate for a weak static scan when static analysis is the purchase requirement. The same rule keeps a wide asset inventory from hiding poor evidence on the one exposed service that prompted the trial. Run each trial with plan-accurate vendor accounts. Snyk product access, test limits, and deployment controls vary by subscription. Aikido's scanners, asset allowances, gating, local controls, and scan frequency also vary by tier. A free trial may show how the interface works without including the features or limits of the plan you would buy. Keep the test repository stable throughout the evaluation. Tag the fixture commit, preserve the lockfile, record scanner settings, and note the exact scan time. When one vendor updates a rule or the code host reruns a job, that record separates a product difference from a changed input. Give the receiving developers the raw finding context. Ask them to locate the affected code, judge the evidence, apply a safe fix, and challenge a false positive. Measure their time and the reviewer handoff. A dashboard score does not show whether a developer can understand, fix, and merge the reported issue. ## Read Next - [Snyk guide](/appsec/snyk/) maps the scanner products, contributor model, integrations, and deployment controls. - [Aikido guide](/appsec/aikido/) maps code, cloud, domain, runtime, and gating features by use case. - [Aikido vs SonarQube](/appsec/comparisons/aikido-vs-sonarqube/) covers self-managed code verification against broader platform coverage. - [Dependabot vs Socket](/appsec/comparisons/dependabot-vs-socket/) narrows the decision to advisory alerts, package intelligence, and supply-chain policy. - [Application Security comparisons](/appsec/comparisons/) places both vendors beside GitHub and Sonar tools. ## Verdict Choose Snyk when editor feedback, product-by-product adoption, and its documented IDE, repository, CLI, and CI integrations are the main requirements. Choose Aikido when the team has decided to consolidate code, dependency, cloud, container, and dynamic checks under one plan. Price Snyk by contributing developer and selected product. Price Aikido by plan, users, and asset allowances. Use the matched-repository trial to decide whether either product can replace a named control. ## Frequently Asked Questions ### Does Snyk charge for every account user? Snyk does not simply charge for every account user. Its current Team offer starts at $25 a month for up to ten developers, with project and test limits. Enterprise usage has separate active-contributor credit rules, so use the billing terms of the plan you are considering. ### Does Aikido scan code inside an IDE? Aikido documents plugins for VS Code, Cursor, Windsurf (now Devin Desktop), Kiro, Google Antigravity, JetBrains IDEs, Visual Studio, and Eclipse. IDE scan types and language coverage vary. Its pricing table says the free IDE path has narrower coverage, so check the current plan row against your language before rollout. ### Do Snyk and Aikido both test running applications? Snyk and Aikido both document dynamic testing capabilities. Snyk API & Web scans web applications and APIs, while Aikido lists domain scanning, API scanning, and authenticated dynamic testing across its plan table. The scan method, stored data, allowances, and required tier differ between the two products. ### Can Snyk or Aikido keep repository credentials on-premises? Snyk Broker keeps source-control credentials inside the customer's network while proxying approved requests to Snyk. Aikido documents local scanning and brokers for internal systems on eligible plans. Those controls reduce data movement, but neither description equals a fully self-managed copy of the entire vendor platform. ### Which Snyk and Aikido costs should a team recheck? Recheck Snyk's plan price, project and test limits, Aikido's included users, repository and asset allowances, annual discounts, and any enterprise deployment add-ons. A purchase order should use a same-day vendor quote. Facts and prices were checked against the official vendor pages on July 28, 2026. For the wider set of options, see our [application security tools guide](/appsec/). --- # Programming ## C# Guide: .NET Apps, Classes, Packages, and Backend Patterns URL: /programming/csharp/ Cluster: Programming > Overview & Programming C# is the statically typed language behind .NET, and it runs web backends, Unity games, and a great deal of Windows software. The frameworks that run on C#, such as ASP.NET Core and Entity Framework Core, have their own guide one level down, because a framework is a separate thing from the language it is written in. Every section below ends with a pointer to where the detail lives. ## What Is C#, in Plain Words? C# is a programming language that Microsoft created for the .NET platform. Microsoft's own tour describes it as a cross-platform, general purpose language based on object-oriented principles, in the C family, so its syntax is familiar to anyone who has seen C, JavaScript, TypeScript, or Java. It calls C# the most popular language for .NET, a free, cross-platform, open source development environment. A statically typed language knows the type of every variable before the program runs, so the compiler or your editor can tell you when you use one wrongly. A runtime is the software that executes your compiled program, and for C# that runtime is .NET. A package is a library someone else wrote that your project downloads, and for C# the packages come from NuGet. C# is compiled, though not straight to machine code. The compiler produces an intermediate form, and the runtime turns that into native code as the program runs, which is why one build runs on several operating systems. The runtime's source is public, and its repository states the MIT licence, which matters to anyone who prefers to read what they run. ## What Is C# Used For, and Where Does It Fit? Reach for C# when you want a fast, typed language with first-class tooling and one vendor-backed platform behind it. Each job in the table below names the part of the platform that does the work, because C# on its own is only the language. | The job | What does the work | Where it runs | | Web backends and APIs | ASP.NET Core, with Entity Framework Core for data | Linux containers or Windows servers | | Games | Unity, whose scripts are written in C# | Wherever the game engine ships | | Windows desktop software | WPF, a Windows-only UI framework | Windows only | | Mobile and cross-platform desktop apps | .NET MAUI, one code base for several platforms | Android, iOS, macOS, and Windows | | Small tools and scripts | File-based apps, new with .NET 10 | Anywhere .NET 10 is installed | Microsoft's documentation says C# programs run on devices from Internet of Things hardware to the cloud, and that you can write apps for phones, desktop computers, and servers. The strongest default for a new backend is a small ASP.NET Core service, which the [frameworks guide](/programming/csharp/frameworks/) walks through. ## Who Uses C#, and Why? The 2025 Stack Overflow Developer Survey puts C# at 27.8 percent of all respondents and 29.9 percent of professional developers, and ASP.NET Core at 19.7 percent of all respondents among web technologies. In the same survey Express was used by 19.9 percent, so the two backend frameworks sit level. The language has been steady across three years: 27.62 percent in 2023, 27.1 percent in 2024, and 27.8 percent in 2025. Stack Overflow's engineering blog says its public platform runs on .NET, with some older applications still on the full .NET Framework that it chose to run on a Windows Kubernetes cluster instead of upgrading before its deadline. Unity's manual says scripts in its engine use C# code, which makes C# the working language of a large number of game developers. Microsoft says most of the .NET runtime and libraries are written in C# itself. | Who | What they say they use it for | | Stack Overflow | Its public platform on .NET, some applications still on .NET Framework | | Unity developers | Game scripts, written as C# code | | Microsoft | The runtime and libraries, most of them in C# | The reasons behind them are consistent: strong tooling, one vendor behind the platform, and a language that suits both servers and game engines. ## Where Did C# Come From, and Is It Still Growing? C# began as Microsoft's language for the original .NET Framework, which was Windows-only. That history shapes what you will find online: a large body of tutorials written for the Windows-only platform, and a newer cross-platform .NET that new work targets. Microsoft's documentation treats .NET Framework as a Windows operating system component, so it is supported without being where new work starts. The [old advice section](#why-old-c-advice-is-dangerous) below shows how to tell the two apart. The current platform moves on a yearly rhythm. Microsoft's support page lists .NET 10 as a long-term-support release supported until November 2028, and .NET 8 and .NET 9 as supported until November 2026. The language version tracks the platform, so C# 14 ships with .NET 10 and the C# 14 notes list features such as extension members and the `field` keyword in properties. On growth, the survey line is flat rather than rising, which I read as a mature language holding its ground. Platform support matters more to that picture than language fashion, and Microsoft's published support dates give a new project a clear runway. ## Is C# Only for Windows and Microsoft Shops? No. The .NET runtime is cross-platform, its repository uses the MIT licence, and Microsoft describes it as a runtime for cloud, mobile, desktop, and IoT apps. A C# service deploys to a Linux container as readily as to Windows. Specific UI technology stays tied to Windows. WPF is documented as Windows-only even on modern .NET. What I like about this arrangement is that I can read the licence, choose the servers the runtime runs on, and keep the build files in plain text. The cost is that the platform has one dominant vendor, so a team should be comfortable following Microsoft's release and support calendar. The table sets out the platform split. | Platform | Runs on | Where new work starts | | .NET (formerly .NET Core) | Windows, macOS, and Linux | Yes, this is the current platform | | .NET Framework | Windows only | No, it is for existing applications | | WPF | Windows only | When the product is a Windows desktop application | | .NET MAUI | Android, iOS, macOS, and Windows | When one code base must cover several devices | ## C# or Java? The two languages are close relatives and the choice rarely turns on language features. Both are statically typed, garbage-collected, and C-family, so the syntax looks alike, and both are mature and widely used. What separates them is the runtime, the owner, and the ecosystem you inherit with each. Pick the one your team already operates, and for a team with no history in either, pick C# when Unity or Windows desktop work is on the horizon. | Question | C# | Java | | Runtime and owner | .NET, backed by Microsoft, under the MIT licence | The JVM, with OpenJDK under GPLv2 plus the Classpath Exception | | Latest release today | C# 14 with .NET 10, supported until November 2028 | JDK 27, generally available on 15 September 2026 | | Survey usage, 2025 (all, professionals) | 27.8 and 29.9 percent | 29.4 and 29.6 percent | The survey rows show how close the pair is. Java led C# among professionals by 1.33 points in 2023 and by 1.2 points in 2024, and C# edged ahead by 0.3 points in 2025, a gap too small to rank on. The [FAQ answer on C# and Java](#are-c-and-java-the-same-language) covers the basics until then. ## C# or TypeScript for a Backend? TypeScript on Node.js and C# on ASP.NET Core can both run a serious backend, and the Stack Overflow survey records them in a close contest: ASP.NET Core was used by 19.7 percent of all respondents in 2025, and Express by 19.9 percent. The main difference is where type checking happens. Node.js can now run TypeScript files by stripping the types, and its documentation states that no type checking is performed when it does, so checking remains a separate step. In C# the compiler checks types every time you build. | Question | C# on ASP.NET Core | TypeScript on Node.js | | Type checking | Done by the compiler on every build | A separate step; Node itself strips types without checking them | | Code shared with the browser | Not by default | Types and logic can be shared | | Packages | NuGet | npm | | Framework choice | One mature default, with EF Core for data | Many options, assembled by the team | | Time to learn | The class library takes time | Gentle for anyone who already writes JavaScript | Choose C# when the backend is the product, several developers will maintain it for years, and you want one framework and compiler checks you cannot forget to run. Choose TypeScript when the browser and the server share code and the team already writes it. The [TypeScript guide](/programming/typescript/) and the [JavaScript guide](/programming/javascript/) cover the other side. ## How Hard Is C# to Learn, and Do Agents Change That? The syntax is approachable, and Microsoft's tour says C# is approachable for beginners while offering advanced features later. Starting is easier than it used to be, because .NET 10 added file-based apps, in which a single file runs without a project around it. The harder part is the .NET class library, which is large, and the framework layer on top of it. ```csharp // hello-world.cs, run with: dotnet run hello-world.cs Console.WriteLine("Hello, World!"); ``` With the .NET 10 SDK installed, that command prints Hello, World! The language order below makes framework code read like ordinary classes. 1. Value and reference types, variables, and constants, so assignment behaves as expected. 2. Nullable value and reference types, so missing data is explicit. 3. Collections such as lists, dictionaries, and arrays. 4. Classes, properties, constructors, and creating objects. 5. Inheritance and interfaces, leaning on interfaces for flexible design. 6. Methods, overloading, and optional and named arguments. 7. LINQ and `async` with `await` once the fundamentals are comfortable. 8. [ASP.NET Core, EF Core, and the wider .NET stack](/programming/csharp/frameworks/). Agents change the cost of typing and help most where the language is strict. Microsoft's tour says every variable has a type known at compile time and that the compiler or editing tools report incorrect use before you run the program, so an agent that misuses a type produces a build error and not a production surprise. In the 2025 Stack Overflow survey 66 percent of respondents named solutions that are almost right, but not quite, as their biggest frustration with AI tools, and a compiler catches only part of that gap. An agent with terminal access can run `dotnet build` and `dotnet test` and read the complaints, which is a useful feedback loop. Nullable reference types add compile-time warnings about missing data, though a forced `!` can still let a null through at run time, so read the diff. Treat the agent as a contractor with a narrow task, as the [terminal AI agents guide](/programming/topics/devops/terminal-ai-agents/) advises, and prefer an open agent such as [OpenCode](/ai-coding-tools/opencode/) if you want to choose the model yourself. ## Is Old C# Advice Safe to Follow? Often it is, but the age of a tutorial matters more in C# than the language suggests. A large body of material was written for .NET Framework, which is still supported as a Windows component yet is no longer where new work starts, so following it can leave you with habits that do not fit the platform you are on. | The old shape | What to know | | `System.Web` and Web Forms | These never moved to modern .NET, so the code needs rewriting against a different framework | | IIS configuration | IIS is not the tell, since ASP.NET Core supports IIS hosting as a first-class option | | A new `HttpClient` per request | Creating and disposing one per request can run a service out of ports under load; use a long-lived client or `IHttpClientFactory` | | `ConfigureAwait(false)` after every await | Mostly unnecessary in ASP.NET Core application code, though still useful in libraries | | A `Program` class with `static void Main` | Still valid, though current C# lets a project start with top-level statements | The reliable tell is the API itself, and not the hosting. Look for `System.Web` or Web Forms before deciding that a tutorial is stale. The [frameworks guide](/programming/csharp/frameworks/) covers the modern request pipeline, package choices, and deployment that these decisions sit inside. ## What Does C# Not Do Well? C# does not give you a Windows-only UI on other systems, since WPF stays on Windows even on modern .NET. It builds iOS and macOS apps through .NET MAUI, but Microsoft's documentation states that building those requires a Mac. Startup time can matter for short-lived command-line tools and serverless functions, where ahead-of-time compilation produces a native binary with a much faster start at the cost of a larger build and limits on reflection-heavy libraries. It also asks you to learn a platform and not only a language. If you are coming from another typed language, the .NET class library is the part that takes time, more than the syntax. A page in a browser still runs JavaScript, so interactivity belongs to the front end however the server is written. ## Which Guides Cover the Framework Side of C#? This site's C# guides start at the framework level. For the language itself, the [Microsoft tour of C#](https://learn.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/) and its beginner tutorials are the best companion. - [C# frameworks: .NET, ASP.NET Core, and EF Core](/programming/csharp/frameworks/) answers how the runtime, the web framework, the ORM, NuGet discipline, testing, and deployment fit together for a real application. - [Essential ASP.NET Core NuGet packages](/programming/csharp/frameworks/aspnet-core-nuget-packages/) answers which packages a production web app needs and which come with the framework already. ## Which Other Languages Should I Compare with C#? C# is the right starting point when a mature, vendor-backed platform and strong tooling fit the team. Each language below answers a different need, and each guide says where it fits. - [Go](/programming/go/) suits a lighter backend language with simpler deployment. - [Rust](/programming/rust/) suits work where memory control matters most. - [Swift](/programming/swift/) suits Apple platforms. - [TypeScript](/programming/typescript/) suits teams that want one language across browser and server. - [SQL](/programming/sql/) covers the databases an EF Core application talks to, and the SQL hub explains which database to start with. ## Which Pages Sit Beside This One? - [C# frameworks](/programming/csharp/frameworks/): .NET, ASP.NET Core, and EF Core in one guide. - [Backend development](/programming/topics/backend/): C# alongside the other server-side languages. - [Programming](/programming/): the broader language and data-structure index. ## Frequently Asked Questions ### What is the difference between .NET Framework and .NET? .NET Framework is the original Windows-only runtime, which Microsoft's documentation treats as a Windows operating system component. .NET, formerly .NET Core, is the current cross-platform runtime that new C# work targets, and it is what the platform means today. ### What is a NuGet package? The package format and package manager for .NET, filling the role npm fills for Node or pip for Python. A project declares the packages it needs and the tooling downloads them and wires up the references. ### Are C# and Java the same language? They are separate languages, although both are statically typed, garbage-collected and C-family, which makes the syntax look alike, but they run on different runtimes, have different owners, and have diverged considerably in language features since they appeared. ### What is the difference between a struct and a class? A struct is a value type, typically stack-allocated and copied whenever it is assigned or passed. A class is a reference type on the heap, so assigning one variable to another shares the same object rather than copying it. ### Is C# compiled or interpreted? Compiled, though not straight to machine code. The compiler produces intermediate language, and the runtime's just-in-time compiler turns that into native code as the program runs. Ahead-of-time native compilation is available where startup time matters. ### Can C# run on Linux and macOS? It can, because the .NET runtime is cross-platform and open source under the MIT licence, so a C# service deploys to Linux containers as readily as it runs on Windows. Some UI frameworks remain Windows-only, including WPF, so the answer depends on the kind of application. ### Which .NET version should a new project use? Use .NET 10, the long-term-support release that Microsoft lists as supported until November 2028. Both .NET 8 and .NET 9 reach the end of their support in November 2026, so an application still on either should plan its upgrade soon. ### Do I need Visual Studio to write C#? You do not need it, because Microsoft's own tour names Visual Studio and Visual Studio Code with the C# Dev Kit as the main editors, and the dotnet command line in the .NET SDK is enough to build and run a program without either of them. ## Where to Start Install the .NET 10 SDK, run a single-file program with `dotnet run`, and then build a small console app such as a task tracker or a CSV parser before touching a framework. Move to the [frameworks guide](/programming/csharp/frameworks/) when you want a web service, and keep the SQL hub open for the database side. --- ## CSS Guide: Selectors, Flexbox, Grid, Typography, and Color URL: /programming/css/ Cluster: Programming > Overview & Programming CSS is the language that tells a browser how a page should look, from colors and type to spacing and where every box sits. [HTML](/programming/html/) supplies the content, and CSS dresses it. You are probably wondering whether Tailwind, component frameworks, and AI assistants made CSS optional. They didn't, though how much CSS you need depends on what you build. ## What Is CSS? CSS, short for Cascading Style Sheets, controls how a web page looks. It sets colors, typography, spacing, and layout by applying rules to HTML elements. Think of HTML as labeled moving boxes and CSS as the instructions for where they go and how they should look. Every CSS rule is one of those notes, and every note has two halves. A selector says which boxes the note is for. A declaration says what to do with them, such as a color, a size, or a gap. Browsers treat each element as a box with its content in the middle, then padding, then a border, then a margin that keeps the neighbors away. Most early spacing bugs come from changing the wrong one of those four layers. Two more words matter before we go on. The cascade is the rulebook for what happens when two notes disagree, and it gets its own section below. Layout is the job of placing the boxes on the page, which is separate from decorating them and has its own family of properties. ## Do I Need to Learn CSS With Frameworks and AI? You still need CSS knowledge when using frameworks or AI assistants. Both can write styles for you, but you need to understand the cascade, sizing, and layout to fix the result. Learn enough to explain why a rule works before trusting it on your own page. Tailwind classes are CSS properties wearing short names. Reading `flex` or `p-4` in someone else's markup means knowing what display and padding do. An AI assistant can draft a layout in seconds, and that is real help. It can also answer a styling bug by piling on more rules, so you need enough CSS to tell a fix from a pile-on. | Helper | What it handles | What you still need | | Tailwind | Writing styles as classes in markup | The CSS properties behind each class | | Component framework | Keeping styles next to components | Selectors, specificity, and inheritance | | AI assistant | Drafting rules and layouts fast | Reviewing what it hands back | Our call is simple. Learn the core of CSS first, which means the box model, the cascade, flexbox, grid, and readable text. After that, let Tailwind or an assistant speed up the typing. Your own CSS knowledge is what lets you review the output and catch a wrong answer before it ships. ## Should I Use Tailwind or Plain CSS? Start with plain CSS if you are learning web development. It teaches the rules that Tailwind utilities depend on. Tailwind makes sense when a team already uses it or you want consistent styling across many screens; its utility classes still produce CSS. | Question | Plain CSS | Tailwind | | Where styles live | In a separate stylesheet | As classes inside your markup | | Naming things | You invent every class name | Utility names come ready-made | | Consistency | Depends on your discipline | Values come from a shared theme | | Learning curve | Slower start, nothing else to learn | Quick once you know CSS names | | Browser floor | You choose it | Chrome 111, Safari 16.4, Firefox 128 | The browser floor is the row people overlook. Tailwind's compatibility page says its fourth version needs Chrome 111, Safari 16.4, and Firefox 128 or newer, so a project that must support older browsers needs a different plan. My pick for a beginner is plain CSS first, because a helper only makes sense once you know what it is helping with. Reach for Tailwind when your team or framework already uses it, or when you are building many screens and want the spacing and colors to stay consistent. You can also mix the two, using utilities for layout and a small stylesheet for the odd custom piece. ## Who Uses CSS, and What Is It Used For? Front-end developers use CSS to style websites, web apps, dashboards, and documentation. Designers and back-end developers also use it when working on browser interfaces. Even a page without an author stylesheet gets default styling from the browser. The people writing CSS are front-end developers first, followed by designers who code and back-end developers who touch templates. If you build anything a human looks at in a browser, you are in that group. ## Which CSS Section Should I Start With? Start with selectors if you are new to CSS, then learn layout and typography. If you are fixing an existing page, choose the section that matches the problem below. | Category | What it covers | Start here if | | [CSS Selectors](/programming/css/selectors/) | Type, class, and id selectors, combinators, pseudo-classes, and specificity | Your rule is written correctly and the element still refuses to change | | [CSS Layout](/programming/css/layout/) | Normal flow, flexbox, grid, and coordinate-based positioning | Your boxes are styled but sitting in the wrong places | | [CSS Typography and Color](/programming/css/typography-and-color/) | Colors, units, fonts, text, custom properties, and shadows | The layout works and the page is still tiring to read | ## Is CSS Still Changing, and Where Did It Come From? CSS is still evolving through separate modules. Work began at CERN in 1994 to separate presentation from HTML, and CSS1 became a W3C Recommendation in December 1996. New layout and styling features continue to build on that foundation. Learn [flexbox](/programming/css/layout/flexbox/), [grid](/programming/css/layout/grid/), and [custom properties](/programming/css/typography-and-color/custom-properties/) before copying old layout workarounds. Modern browsers support these fundamentals, while newer features still need checking against the browsers your project supports. ## Which Old CSS Advice Should I Ignore? Avoid using floats for whole-page columns, absolute positioning for ordinary page structure, or repeated !important declarations to force a style. Modern layout tools handle those jobs more reliably. Keep an old workaround only when you understand the browser or layout problem it solves. Floats were designed for wrapping text around an image, then got pressed into building whole page columns, which is why older stylesheets carry a clearfix rule. Use [flexbox](/programming/css/layout/flexbox/) for one direction and [grid](/programming/css/layout/grid/) when rows and columns must agree. A pile of `!important` usually marks a specificity fight nobody won, and [understanding specificity](/programming/css/selectors/specificity/) removes most reasons to write it. Vendor prefixes were added when browsers shipped experimental properties under their own names, so check whether a prefix is still needed by your supported browsers before removing it. The tell is the same each time. If a snippet works around something that has since gained a real property, the workaround costs you clarity for no benefit. ## Which Modern CSS Features Are Worth Learning? After the basics, learn container queries for reusable responsive components, :has() for relational styling, and native CSS nesting for related rules. HTML features such as popover can also replace some scripted interface behavior. Check support before depending on newer features. | Feature | Use It For | | `:has()` | Style an element based on related elements, such as a card containing an image. | | Container queries | Adapt a component to its available space instead of the entire viewport. | | CSS nesting | Group related rules inside a parent rule without a preprocessor. | | HTML `popover` | Show and hide an overlay using built-in HTML behavior, then style it with CSS. | | `@starting-style` | Set starting styles for entry transitions. | A feature working in your browser does not guarantee it works for every visitor. Use the compatibility information in its documentation and keep the page usable when a newer enhancement is unavailable. JavaScript still owns anything that needs data, decisions, or events beyond hover and focus. CSS handles the state the browser already tracks. AI assistants can also reach for a script out of habit, because plenty of older examples did. When you review a snippet that toggles a class to open a menu, ask whether a popover or a details element would already do the job. ## Is CSS Hard to Learn, and How Long Until I Am Useful? CSS basics are approachable, but reliable layouts take practice. A first weekend can be enough to style a simple page; responsive layouts usually need repeated practice over weeks or months. The syntax itself uses a selector, curly braces, a property, and a value. The hard parts are mental models, meaning how boxes size themselves, how the cascade picks a winner, and how flexbox and grid share space. Like a Jersey diner menu, CSS has a very long list of properties, and nobody orders them all. You will use the same dozen again and again and look up the rest. Results vary a lot by person, so read this table as a rough guide for someone practicing most days, never as a promise. | Time you put in | What you can do | Start with | | First weekend | Style a simple bio page | [Selector basics](/programming/css/selectors/selector-basics/) | | First few weeks | Lay out a real page | [Flexbox](/programming/css/layout/flexbox/) | | First couple of months | Build responsive layouts and themes | [Grid](/programming/css/layout/grid/) and [custom properties](/programming/css/typography-and-color/custom-properties/) | Agents change the middle of that curve. An assistant can draft the flexbox row in seconds, so you spend less time memorizing property names and more time reading what comes back. I think that makes the cascade and the box model more valuable, because they are how you spot a wrong answer. ## How Does the CSS Cascade Choose a Style? The CSS cascade picks a winning declaration for each property on an element. Origin, importance, and cascade layers are considered before selector specificity. Scoping proximity can then break a tie, followed by source order. When all earlier conditions tie, the later declaration wins. ```css p { color: gray; } .intro { color: navy; } ``` A paragraph with `class="intro"` matches both rules and renders navy. A class selector outranks a type selector at the specificity stage, and specificity is settled before source order is ever consulted. Swap the two lines and the paragraph is still navy. This is why a declaration can look completely correct and still do nothing. The property is valid, the value is valid, and another rule simply won. Once you can read the cascade, you stop guessing at that whole class of bug, and the [CSS specificity guide](/programming/css/selectors/specificity/) gives you the scoring. ## How Do CSS Selectors Target Elements? CSS selectors target elements by features such as their tag, class, ID, attributes, or state. A valid selector can still match nothing if it describes the wrong element. Start with type, class, and ID selectors, then add relationships only when you need them. | Selector | Use it for | Watch out for | | Type | Defaults for every element of one kind | Matching more than you meant | | Class | Most real styling, reusable anywhere | Vague names that collide later | | Id | One truly unique element | High weight that is hard to override | Classes carry the overwhelming majority of real styling because they are reusable and their specificity stays predictable. After that, combinators target elements by their relationships, and pseudo-classes pick out states such as hover or the first item in a list. The [CSS selectors guide](/programming/css/selectors/) works through each in order. ## How Do You Arrange a Page With CSS? Use normal flow for ordinary text, flexbox for rows or columns, and grid when both dimensions need coordinated tracks. Positioning handles overlays and sticky or fixed controls. Start with a sensible HTML order so the page still reads correctly before those layout rules apply. | Question | Flexbox | Grid | | Direction | One axis at a time | Rows and columns together | | Best for | Nav bars, button groups, card rows | Page-level structure | | Sizing starts from | The content | The layout you define | Content sharing one line is a flexbox job, and a structure where alignment must hold in two directions is a grid job. Positioning handles special placement, such as a sticky header that retains its space or an absolutely positioned badge removed from normal flow. Using it as a general layout tool is a common early mistake, and the [CSS layout guide](/programming/css/layout/) explains why. ## How Can CSS Make Text Easier to Read? CSS makes text easier to read through an appropriate font size, comfortable line height, manageable line length, and clear foreground/background contrast. Check those choices together at narrow widths and with browser zoom. A tidy layout cannot rescue unreadable text. Units matter just as much as type size does, and a `rem` size follows the root font size. Leave that root tied to the browser default to respect the reader’s text-size preference; browser zoom can scale pixel-based text too. Custom properties are the piece that changes how a stylesheet ages, because naming a color once means a theme or dark mode becomes a change in one place. Shadows suggest depth and are easy to overdo, so borders stay the more reliable way to separate one surface from another. The [CSS typography and color guide](/programming/css/typography-and-color/) covers each of these. ## Which CSS Guide Covers Your Task? Use the flexbox guide for alignment, grid for coordinated rows and columns, and specificity for rules that will not apply. The task links below take you straight to the relevant example. | You want to | Go to | | Center something, horizontally or vertically | [Flexbox](/programming/css/layout/flexbox/) | | Build a page-level structure with aligned rows and columns | [Grid](/programming/css/layout/grid/) | | Work out why your rule is being ignored | [Specificity](/programming/css/selectors/specificity/) | | Style a hover, focus, or first-item state | [Pseudo-classes](/programming/css/selectors/pseudo-classes/) | | Pin a header so it stays visible while scrolling | [Positioning](/programming/css/layout/positioning/) | | Reuse one color or spacing value across a stylesheet | [Custom properties](/programming/css/typography-and-color/custom-properties/) | | Set a comfortable line height and font size | [Fonts and text](/programming/css/typography-and-color/fonts-and-text/) | | Choose a color format or a sizing unit | [Colors and units](/programming/css/typography-and-color/colors-and-units/) | ## What Should You Learn First in CSS? Learn CSS selectors and the cascade first, then the box model, normal flow, flexbox, and grid. Add typography and color as you build a small page. That order gives you the tools to understand and fix each example. 1. [Selector basics](/programming/css/selectors/selector-basics/), because you cannot style anything you cannot reliably target. 2. [Specificity](/programming/css/selectors/specificity/), because it explains the ignored-rule problem that otherwise costs you hours. 3. [Display and normal flow](/programming/css/layout/display-and-flow/), because it is the default every layout tool modifies. 4. [Flexbox](/programming/css/layout/flexbox/), which handles most day-to-day arrangement work. 5. [Colors and units](/programming/css/typography-and-color/colors-and-units/), once the structure holds and readability becomes the problem. 6. [Grid](/programming/css/layout/grid/) and [custom properties](/programming/css/typography-and-color/custom-properties/), when pages grow past a few screens. ## What Can't CSS Do? CSS styles what the document already contains. It cannot add, remove, or reorder real elements, so anything that changes the content itself belongs to [HTML](/programming/html/) or [JavaScript](/programming/javascript/). Visual order can be changed with flexbox and grid, and doing so does not move the element for a screen reader or for keyboard focus, which is why reordering is a decision to make carefully. CSS can load assets such as images and fonts, but it does not handle application tasks such as saving a form or processing an API response. HTML controls provide some interactions without JavaScript. Scripts handle behavior that those built-in controls and CSS states cannot express. ## CSS Guides by Topic ### Selector Guides These four guides sit under the [CSS selectors hub](/programming/css/selectors/) and read best in this order. - [CSS selector basics](/programming/css/selectors/selector-basics/) covers type, class, and id selectors, and when each one is the right pick. - [CSS combinators](/programming/css/selectors/combinators/) target elements through parent, child, and sibling relationships. - [CSS pseudo-classes](/programming/css/selectors/pseudo-classes/) select states and structural positions such as hover and first item. - [CSS specificity](/programming/css/selectors/specificity/) explains which matching selector wins and why. ### Layout Guides These four guides sit under the [CSS layout hub](/programming/css/layout/) and build on normal flow. - [CSS display and normal flow](/programming/css/layout/display-and-flow/) compares block, inline, and inline-block boxes. - [CSS flexbox](/programming/css/layout/flexbox/) arranges and aligns items along one axis. - [CSS grid](/programming/css/layout/grid/) coordinates rows and columns together. - [CSS positioning](/programming/css/layout/positioning/) covers relative, absolute, fixed, and sticky placement. ### Typography and Color Guides These four guides sit under the [CSS typography and color hub](/programming/css/typography-and-color/) and cover the readable surface. - [CSS colors and units](/programming/css/typography-and-color/colors-and-units/) explains color formats and sizing values. - [CSS fonts and text](/programming/css/typography-and-color/fonts-and-text/) controls typefaces, sizing, line height, and alignment. - [CSS custom properties](/programming/css/typography-and-color/custom-properties/) reuse authored values through variables and themes. - [CSS shadows](/programming/css/typography-and-color/shadows/) builds box, text, and alpha-shaped shadows. ## Related Frontend Guides - [HTML and CSS together](/programming/html-css/) is the comparison that explains why you learn HTML first and CSS second. - [HTML](/programming/html/) supplies the structure that every CSS rule selects against. - [JavaScript](/programming/javascript/) takes over when a page needs behavior rather than presentation. - [Frontend development](/programming/topics/frontend/) puts CSS in the context of the wider role and gives one learning order. - [The programming guide](/programming/) covers the other languages on the site. ## Frequently Asked Questions ### Is CSS a programming language? CSS is a stylesheet language that describes presentation rather than a general-purpose programming language. It has calculations and conditional styling, but it does not run application logic the way JavaScript does. ### Can an AI assistant write all my CSS? An AI assistant can draft CSS, but you still need to review its output. Check for conflicting rules, unnecessary styles, unreadable text, and layouts that break on small screens. ### What is the difference between CSS and a CSS framework? CSS is the language that browsers read and apply. A framework such as Tailwind is a toolkit of ready-made classes that ends up as CSS in the browser. You can write CSS with no framework at all, but every framework still ships CSS. ### Should I learn CSS before JavaScript? Most beginners building websites should learn CSS before JavaScript, after learning HTML. CSS lets you style the page that JavaScript will later change. If you only want to build back-end services, CSS can wait. ### Does declaration order inside a CSS rule matter? Declaration order matters when CSS declarations set the same property or when a shorthand overlaps a longhand. Among valid declarations of equal importance, the later declaration wins. Unrelated properties can usually appear in any order. ### Can CSS change content as well as appearance? CSS can add decorative text or icons through generated content such as ::before and ::after, but it does not create or rearrange real HTML elements. Use HTML or JavaScript for meaningful content and changes to the document structure. ### Is Sass the same thing as CSS? Sass is a separate language that compiles into CSS before the browser reads it. Sass still offers features such as mixins and loops, while modern CSS provides its own custom properties and nesting. ### Do browsers support new CSS features at the same time? Browsers do not introduce new CSS features at the same time. Check support in the browsers your readers use before relying on a new feature, and provide a usable fallback when needed. --- ## Go Guide: CLIs, Backend Services, Concurrency, and Tooling URL: /programming/go/ Cluster: Programming > Overview & Programming The language looks small because it is small on purpose. You get packages, functions, structs, interfaces, explicit errors, goroutines, channels, and a standard library that covers a surprising amount of backend work. The trade-off is that Go asks you to be boring in the places where other languages invite cleverness. That restraint is a compliment when the code needs to compile fast, deploy as a single binary, run for years, and stay readable after three different people have touched it. Go powers infrastructure, command-line tools, network services, internal platforms, and API backends because the language keeps the moving parts visible. Start with modules and plain functions, then learn structs, interfaces, and explicit errors. Frameworks can wait until you understand what the standard library already gives you. ## What Is Go, in Plain Words? Go is a programming language for software that runs on servers, in the cloud, and at the command line. Google engineers designed it, and the Go project describes it as a fast, statically typed, compiled language that feels like a dynamically typed one. You write your code, run one command, and get a single program file you can copy to another machine. Picture a transit map hanging on a station wall. It leaves out the streets, the hills and every building, and nobody calls it worse for that, because you can still find your stop. Go works the same way, since it leaves out most of the features other languages pile on, so you can find your way through code that someone else wrote. Four terms come up everywhere on this page: - Compiled: your code is translated into a program the computer runs directly, before anyone runs it. - Statically typed: every value has a type, such as a number or text, and the compiler checks them before the program starts. - Garbage collected: Go frees memory for you while the program runs, so you never free it by hand. - Standard library: the large set of ready-made packages that ships with Go, from web servers to JSON. Nothing here assumes you already know another language. If you have met a variable and a function anywhere, you are ready. ## Where Go Fits Go is strongest when you want predictable performance, simple deployment, and code that stays readable as a team grows. That covers API backends, command-line tools, network services, build and ops tooling, and the kind of internal platforms that have to keep running without drama. It is less of a natural fit when you want rich language features or a deep object model. Go deliberately leaves out a lot, and that bothers people who want the language to express more. If you need quick data scripting, [Python](/programming/python/) gets you there faster. If you need maximum control over memory, [Rust](/programming/rust/) gives you more. ### Who Should Learn Go? When I read through the 2025 Go Developer Survey, one number stood out. 81% of respondents had more professional experience than Go experience, so most people meet Go as their second or third language. That tells you who the page is really for: - New to programming and aiming at servers or tools: Go works as a first language if you like small languages. If you want pictures on a web page by tonight, start with [JavaScript](/programming/javascript/) instead. - Coming from Python or JavaScript: you gain types, a compiler that catches slips early, and one program file to deploy. You also give up some flexibility, and handling errors is where that change feels biggest. - Working in DevOps or cloud operations: much of the tooling you already run is written in Go, so reading it becomes a real skill. - Doing data science or machine learning: stay with Python, because Go plays a supporting role around that work. ## What Do People Build with Go, and Who Uses It? The Go project lists four main jobs: cloud and network services, command-line tools, web development, and DevOps and site reliability work. The 2025 Go Developer Survey, with 5,379 respondents, points the same way. Command-line tools and API services came out on top, 55% of respondents build both, and over a third build cloud infrastructure tooling. Companies tell the same story in their own words. This table covers what each one said, and the dates matter because the older posts explain why teams chose Go in the first place. | Who | Used Go for | Source and year | | Dropbox | Performance-critical backends | Company blog, 2014 | | Cloudflare | DNS, compression and SSL | Company blog, 2013 | | Monzo | Banking microservices | Company blog, 2016 | | Kubernetes, Docker, Terraform | Main language of each repository | GitHub, checked 2026 | Dropbox wrote in 2014 that it moved performance-critical backends from Python to Go for better concurrency support and faster execution, about 200,000 lines of Go in all. Cloudflare said in 2013 that Go was at the heart of its DNS, compression and SSL services. Monzo wrote in 2016 that Go suits microservices and would probably stay its dominant language. Three reasons repeat: easy concurrency, quick execution, and servers that stay simple to read. Today GitHub lists Go as the main language for Kubernetes, Docker's moby, Terraform and Prometheus, which is current data and a better sign that the choice lasted than any old blog post. ## How Did Go Start, and Where Is It Now? The history matters here because it changes what you trust when you search for help. Each date marks a kind of advice that has since gone stale. - Three Google engineers, Robert Griesemer, Rob Pike and Ken Thompson, started sketching Go on a whiteboard on 21 September 2007. They were frustrated with the complexity of building large software and wanted real support for multicore machines. - Go became a public open source project on 10 November 2009. - Go 1 shipped on 28 March 2012 with a promise that programs written for it keep compiling. That promise is why old code still runs, and why old advice still compiles even when it is no longer the best way. - Go 1.11 (August 2018) added preliminary support for modules, and Go 1.16 (February 2021) made them the default. Any tutorial that starts by setting GOPATH comes from before that, and today's workflow starts from a `go.mod` file instead. - Go 1.18 (March 2022) added generics, so older answers that reach for empty interfaces to share code come from before it. - Go 1.27 (August 2026) is the current release, and the next section lists what changed on the way there. ## Is Go Popular, and Is It Growing? It is growing at a steady pace, according to the one broad source I trust for this. The Stack Overflow Developer Survey asked the same question in 2024 and 2025, and every language below gained ground among respondents. | Language | Used in 2025 | Used in 2024 | | Python | 57.9% | 51% | | TypeScript | 43.6% | 38.5% | | Go | 16.4% | 13.5% | | Rust | 14.8% | 12.6% | Read that table with some care, because it covers survey respondents only, and the respondent mix changes from year to year. Go also scored 56.5% on the survey's "admired" measure, meaning people who used it and want to keep using it. The Go team's own 2025 survey adds two details. 91% of respondents said they were satisfied with Go, a figure the team says has stayed steady since 2019. And only 13% had used Go for less than a year, down from 21% in 2024, which the team suspects reflects fewer entry-level hiring slots. So Go is steady and well liked, and it does not rely on a stream of first-time programmers. ## Is Go Easy to Learn, and Do Agents Change That? Go is small enough to hold in your head, and that helps. The official docs point beginners to [A Tour of Go](https://go.dev/tour/), an interactive course in four parts: basic syntax and data structures, methods and interfaces, generics, and concurrency. Even a small language has surprises, and slices and goroutines trip up almost everyone the first time. The same survey reports where the friction sits. The top frustrations were following Go idioms (33%), missing a feature from another language (28%), and finding trustworthy modules (26%). Because most respondents learned Go after another language, the pain comes from unlearning habits such as exceptions and inheritance. Agents change the picture less than the hype suggests. The 2025 Stack Overflow survey reported 84% of respondents using or planning to use AI tools, yet 46% distrust the accuracy of the output and 33% trust it. The Go survey reported that most Go developers use AI tools too, with middling satisfaction partly over quality concerns. So the skill that matters is reading. Let an assistant draft the first version, then go through every `if err != nil` branch yourself. A small language with one formatter and explicit errors is a good one to review, because there is less hidden behavior to miss. ## Should I Use Go or Rust for This Project? If your team needs a service or a command-line tool quickly and wants every teammate able to read it, start with Go. If you need control over memory, no garbage collector pauses, or code that sits close to the operating system, Rust earns its extra learning time. | You care about | Go | Rust | | First working service | Small language, quick start | Ownership first, slower start | | Memory and latency | Garbage collector handles memory | You control it, no collector | | Concurrency | Goroutines built into the language | Threads, plus async crates | | Reading a teammate's code | Few features to learn | More concepts, stricter compiler | | Shipping | One program file | One program file | Real teams have gone in both directions over the years. Discord wrote in 2020 that its Read States service, written in Go, showed latency spikes about every two minutes, which it traced to garbage collection, and a Rust version had none. That post is six years old, and Go's collector has changed since, including the Green Tea collector that became the default in Go 1.26. Measure your own service before you rewrite anything. OpenAI chose Rust over TypeScript for its Codex command-line tool in 2025, partly for an install with no Node dependency. Go also builds a single program file, so a one-file install alone does not pick Rust. ## Go, Rust, Python or TypeScript for Common Jobs? This table is my call for a beginner choosing a first tool for a job, based on what the official sources say each language is for. Plenty of teams mix them, and no single language lands anyone a job. | Job | Start with | Why | | JSON API for a product | Go or TypeScript | Simple servers, big ecosystems | | Data analysis and AI work | Python | That is where the libraries live | | Website front end | TypeScript or JavaScript | Browsers run them natively | | Command-line tool to share | Go or Rust | One file, no runtime to install | | Performance-critical core | Rust | No collector, tight control | | Quick automation script | Python | Fastest from idea to result | | First phone app | Swift or Kotlin | Native to the platform | GitHub's Octoverse 2025 reports that TypeScript overtook Python and JavaScript in August 2025 to become the most used language on GitHub by contributor count. That says a lot about how much of the web's work is TypeScript, so a Go service often sits behind a TypeScript front end. ## What Rule Does the Rest of Go Depend On? Go copies what you hand to a function. Most of the rest of Go leans on that, and it is rarely said out loud. The program below hands one shopping cart to a function that tries to change two things. ```go package main import "fmt" type Cart struct { Items []string Total int } func addItem(c Cart) { c.Items[0] = "changed" c.Total = 99 } func main() { cart := Cart{Items: []string{"milk", "eggs"}, Total: 2} addItem(cart) fmt.Println(cart.Items, cart.Total) } ``` ```text // output: // [changed eggs] 2 ``` The function got its own copy of the cart, so its change to the total vanished. Its change to the first item stuck, because a copied slice still points at the same list of items. To change the original total, you hand over a pointer to the cart instead of a copy. Pointers, slices and method receivers all grow out of that one rule, which is why slices and pointers confuse so many newcomers. ## What Changed in Go from 1.21 to 1.27? Go ships two releases a year, in February and August. Go supports each major release until two newer ones exist, so Go 1.27 and Go 1.26 are the supported versions today, and the latest 1.27 point release is 1.27.1. Here is what changed, in the order that matters for what you will read online: - Go 1.21 (August 2023): the built-in `min`, `max` and `clear`, the `slices` and `maps` packages, structured logging in `log/slog`, and a `go` command that can fetch the toolchain a project asks for. - Go 1.22 (February 2024): each loop iteration gets its own variables, loops can range over integers, and the standard router matches HTTP methods and wildcards. - Go 1.23 (August 2024): loops can range over functions, which is how custom iterators work. - Go 1.24 (February 2025): `go.mod` gains a `tool` directive for tools your project depends on. - Go 1.25 (August 2025): `sync.WaitGroup` gets a `Go` method, and `GOMAXPROCS` respects container CPU limits. - Go 1.26 (February 2026): `new` accepts a starting value, `errors.AsType` arrives, `go fix` is rebuilt around modernizers, the Green Tea garbage collector becomes the default, and `go mod init` writes the previous release's version. - Go 1.27 (August 2026): methods can declare their own type parameters, `encoding/json/v2` and a `uuid` package join the standard library, the `goroutineleak` profile is generally available, and timer channels are always unbuffered. The [Go 1.27 release notes](https://go.dev/doc/go1.27) say the usual compatibility promise holds and almost all programs keep compiling as before. Go 1.28 is expected in February 2027, and its draft notes mention new vet checks for ignored scanner and database row errors. A draft can change, so treat that as a preview. Any answer that copies a loop variable by hand, installs a third-party router for simple routes, or sets up GOPATH comes from before these changes. Check the release notes for your version before you copy code from an older answer. ## Learning Path Learn Go in the order that makes the standard library feel like enough: 1. Variables, basic types, constants, and explicit conversions. 2. Slices, maps, and the `for range` loop that walks them. 3. Functions, multiple return values, and the `(value, err)` pattern. 4. Explicit error handling with `if err != nil` and wrapping. 5. Structs, methods, and the difference between value and pointer receivers. 6. Interfaces satisfied implicitly, kept small and focused. 7. Modules, packages, and the `go` toolchain as the everyday workflow. 8. Goroutines, channels, and `context` once the sequential code is solid. When the basics stop feeling new, build something small that actually runs. A command-line tool or a tiny JSON API over `net/http` will teach you types, errors, and interfaces without a framework in the way. ### When Are You Ready for the Next Step? The eight steps above, plus testing and web services, make a nine-step path. Install Go before you start step one, and let the right-hand column tell you when you are ready to move on. | Step | Move on when you can | | 1. Language basics | Write a program with a loop | | 2. Collections | Store items and look them up | | 3. Functions | Return a result and an error | | 4. Errors | Wrap an error and check it | | 5. Types | Define a struct and a small interface | | 6. Modules and tooling | Add a dependency to a module | | 7. Testing | Run a table of test cases | | 8. Concurrency | Start a goroutine and stop it | | 9. Web services | Serve JSON from a handler | ## What Mistakes Do Beginners Make in Go? Most of these mistakes are Go refusing to let a slip through, which is a favor once you get used to it. | Mistake | What Go does | Fix | | Leaving a variable or import unused | Refuses to compile | Delete it or use it | | Writing to a nil map | Panics at run time | Create it with make first | | Ignoring a returned error | Carries on with a zero value | Check the error at once | | Changing a value receiver | Edits a copy only | Use a pointer receiver | | Mixing int and float64 | Mismatched types error | Convert one explicitly | | Sending with no receiver | Deadlock, all goroutines asleep | Pair every send with a receive | | Copying old tutorials | Outdated habits creep in | Check the Go version | | Reaching for a framework first | Extra dependencies to maintain | Start with the standard library | Two of those deserve a closer look. Ignoring an error is the sneaky one, because the program keeps running with a zero value where your data should be, and nothing tells you. Starting a goroutine with no way to stop it is the slow one, because the leak only shows up after days of load. ## What Should I Learn First? If you only have an evening, do this in order and skip everything else for now: 1. Install Go and run `go version`, following the install instructions on go.dev. 2. Write a small program with variables, a loop, and one function that returns a value. 3. Add a slice and a map, because nearly every real program leans on both. 4. Make a function return an error, and check it at the call site. 5. Format with `go fmt` and add one test you can run with `go test`, so those habits start on day one. 6. Leave structs, interfaces and goroutines until the earlier steps feel ordinary. Go is a language where the boring path really is the fast one. Your first program should look like everyone else's first program, and that is a feature. ## What to Build First Build a small command-line tool or JSON API with real input and real failure paths. A URL shortener or a word-count tool is enough if you treat it seriously: parse input, return an `error` from anything that can fail, check `if err != nil` at each step, and model the data with a struct. Run down this short list so the exercise actually teaches you something: - Declare values with `:=` inside functions and `var` where zero values matter. - Return an `error` from fallible functions, and handle it at the call site. - Pick a slice for ordered data and a map for keyed lookup. - Define a small interface only when two types genuinely share behavior. - Run `go fmt`, `go vet`, and `go test` before you trust the code. - Have an AI assistant draft its own version, then read every `if err != nil` branch closely. When that feels ordinary, goroutines, channels, and the bigger standard-library packages stop looking like tricks and start looking like the next plain tool in the box. ## What Can't Go Do? Go fits some jobs better than others, and the survey says as much: 28% of respondents named a feature they value from another language that Go leaves out. Here are the limits that matter most: - Desktop and phone apps: the standard library ships no GUI toolkit, and a first mobile app belongs in Swift or Kotlin. - Data science and machine learning: Python dominates it, and GitHub's Octoverse 2025 says Python powers nearly half of all new AI repositories. - Browser front ends: that is JavaScript and TypeScript territory. - Class hierarchies: Go has no inheritance and no exceptions, by design, and the Go FAQ explains why. - Hand-tuned memory control: the garbage collector decides when memory is freed, and Rust gives you that decision. Go is mostly for servers and command-line tools, and that focus is why the tooling is so good. It also reaches into cloud infrastructure and, for 11% of survey respondents, machine learning tools and agents. ## Related Languages Go is the right starting point when you want a simple, fast backend language that a team can maintain for years. The restraint is the part that surprises people coming from richer languages, and it is the whole point. Reach for [Python](/programming/python/) when scripting and data work matter more than deployment shape. [Rust](/programming/rust/) is the choice when you need memory control and are willing to make ownership explicit. [Swift](/programming/swift/) and [C#](/programming/csharp/) fit teams centered on Apple platforms or .NET. Many backend teams keep Go for services and reach for another language only where it clearly earns its place. ## Related - [Rust](/programming/rust/) - more control over memory and performance. - [Python](/programming/python/) - faster for scripts and data work. - [Programming](/programming/) - the broader language and data-structure index. - [Backend development](/programming/topics/backend/) - how servers, APIs and databases fit together in any language. - [APIs](/programming/topics/apis/) - the request, response and status-code ideas behind every Go handler. - [DevOps](/programming/topics/devops/) - the wider picture of building, shipping and running programs. ## Frequently Asked Questions ### Does Go have exceptions? No. A function returns an error as an ordinary value alongside its result, and the caller checks it directly. panic and recover exist for unrecoverable situations, so idiomatic Go does not use them as a substitute for try and catch. ### Is Go garbage collected? Yes, with a concurrent collector that runs alongside the program, and since Go 1.26 the default one is called Green Tea. Rust takes the other route and tracks ownership at compile time with no collector at all. Go trades some control over when memory is freed for markedly simpler code. ### Can you build a desktop application in Go? Not with the standard library, which ships no GUI toolkit. Projects such as Fyne and Wails add one, but Go's tooling and ecosystem are built around servers, command-line tools, and network services rather than desktop interfaces. ### Does Go support generics? Yes, generics arrived with Go 1.18, released back in March 2022. Functions and types can take type parameters, which closed a long-standing gap where reusable containers and algorithms meant reaching for empty interfaces or generating code. Go 1.27 (August 2026) added generic methods, so a method can now declare its own type parameters. ### What is a goroutine leak? A goroutine blocked forever on a channel, lock, or condition that will never arrive, so it never exits and its stack stays allocated. Leaks accumulate quietly under load, which is why every goroutine needs a clear exit path. Go 1.27 added a goroutineleak profile that reports a large class of them. ### Is Go a good first language? It can be, if you want to build servers or command-line tools and you like a small language. Most Go developers arrive from another language, since 81% of 2025 survey respondents had more professional experience than Go experience. If you want results in a browser quickly, start with JavaScript. ### Should I learn Go or Rust first? Learn Go first if you want working programs quickly, because it has fewer concepts to absorb. Learn Rust first only when memory control or having no garbage collector is the point of your project. Many people learn Go first and add Rust later when a project calls for it. ### Is Go still growing? Yes, going by the Stack Overflow Developer Survey, where Go use among respondents rose from 13.5% in 2024 to 16.4% in 2025. The Go team's own 2025 survey reported 91% of respondents satisfied, though fewer newcomers, which the team links to slower entry-level hiring. ### Which Go version should I learn on? Install the current release, Go 1.27, which came out in August 2026. Go supports each major release until two newer ones exist, so Go 1.26 is also supported. Check go.dev for the latest point release before you install. ### Is it called Go or Golang? The language is called Go, and the Go FAQ says the golang name arose because the website was originally golang.org, with no .dev domain available at the time. You can search for golang, but write Go in your own pages. ## Where to Start Install Go, run `go version`, and write one small program that returns an error on purpose. Then work down the learning path above one step at a time. Build the tool that scares you a little, because the way it fails is the lesson. --- ## HTML & CSS: How the Web's Two Core Languages Fit Together URL: /programming/html-css/ Cluster: Programming > Overview & Programming Every page on the web is built from the same two languages. HTML says what things ARE: a heading, a paragraph, a table of prices, a form that collects an email address. CSS says what things LOOK LIKE: the column layout, the spacing, the colors, the type. They ship in separate files, they follow separate rules, and they reward [learning in a particular order](/programming/), which is why each has its own full guide here. ## The Division of Labor HTML is structure and meaning. When you mark a line as a heading instead of just making text big, browsers, search engines, and screen readers all understand the page better. Get the structure right and a page works before a single line of styling exists. CSS is presentation. It takes that structure and decides how it renders on a phone, a laptop, and everything in between. One stylesheet can restyle an entire site without touching the content, and that separation is the whole trick. Change how it looks without changing what it says. The languages meet through selectors. CSS points at HTML elements by their tag names, classes, and structure, so well-organized HTML makes CSS shorter and saner. Messy HTML makes every stylesheet a fight. ## Which to Learn First Start with HTML. It is the smaller language, you can build a complete working page with it alone, and everything CSS does assumes an HTML structure to point at. A few days of plain HTML makes CSS click much faster, because you already know what all those selectors refer to. Then move to CSS and expect it to take longer. Layout, the cascade, and responsive design have more moving parts than markup does. That is normal, and the guide breaks it into pieces that build on each other. ## How They Connect in Practice A page links its stylesheet with one line in the HTML head, and from that moment the two files have a contract. HTML supplies class names and structure; CSS selects on them. Keep class names describing what a thing IS (`price-table`, `site-nav`) rather than how it looks (`red-text`, `big-box`), and the stylesheet can change completely without touching a single line of markup. When several CSS rules point at the same element, the cascade decides which one wins, using specificity and source order. That sounds abstract until the first time a style refuses to apply, and then it is the most practical thing you know. The CSS guide covers the resolution rules, because guessing at them is where most beginner frustration comes from. ## The Mistakes to Skip Two habits cause most beginner pain. The first is styling before the structure is finished, which leaves you writing CSS that fights markup you are about to change, twice. Finish the plain HTML version of a section, then style it. The second is using HTML for looks, like marking text as a heading because headings render big. Browsers, search engines, and screen readers all treat that as a lie about the page's structure. If you want big text, that is a CSS job; if it starts a section, it is a heading. ## Frequently Asked Questions ### Can you write CSS inside an HTML file? Yes, either in a style block in the head or a style attribute on a single element. A separate linked stylesheet is still preferred beyond a one-page demo, because the browser can cache it and every page can share it. ### Do you need a build tool to use HTML and CSS together? No. A plain HTML file linked to a plain CSS file works in any browser with nothing compiled. Build tools earn their place once a project needs component reuse or bundling, not at the start. ### Does a page work with no CSS at all? Yes. Browsers apply a default stylesheet, so unstyled HTML still renders with visible headings, spacing, and link colors. What you get is the browser's opinion rather than a design decision. ### What is a CSS reset? A small stylesheet loaded first that removes or standardizes each browser's own defaults, such as margins and list bullets, so your CSS starts from one predictable baseline instead of arguing with inconsistent starting values. ## Where to Go Next - The [HTML guide](/programming/html/) starts with document structure and semantic elements, then works through [tables](/programming/html/tables/), forms, and the browser-native patterns every page depends on. - The [CSS guide](/programming/css/) covers selectors, [typography and color](/programming/css/typography-and-color/), layout, and how the cascade decides which rule wins. When you can build a structured page and style it responsively, the natural next step is making it interactive, and that is where [JavaScript](/programming/javascript/) picks up. --- ## HTML Guide: Page Structure, Elements, and Tables URL: /programming/html/ Cluster: Programming > Overview & Programming HTML is the markup that gives a web page its structure. Before any styling or scripting, it names what each piece of content is, such as a heading, a paragraph, a list, a table, a form, or a navigation bar. Browsers, search engines, and screen readers all lean on those names, which is why [learning the elements](/programming/html/elements-and-structure/) comes first. Learn HTML before a framework so you can read the structure the framework produces. The choice of elements still matters when AI writes the markup. ## What Is HTML? HTML stands for HyperText Markup Language. It describes web content with elements such as headings, paragraphs, links, and images. The browser reads that markup to build the document. [CSS](/programming/css/) controls its appearance, while JavaScript adds custom behavior. Open View Page Source on a site and look for familiar tags. Even when a framework generates the page, understanding those tags helps you read the result. Semantics means the name of an element says what its content is, so a `nav` element means navigation while a plain `div` means nothing. The parser is the part of the browser that reads your file and builds the page. Accessibility means the page works for people using screen readers, keyboards, or other assistive tools. ## Is HTML a Programming Language, and Does It Matter? HTML is a markup language, not a programming language. It describes content rather than running variables, loops, and conditions. That distinction tells you what to learn next when a page needs more than structure. For a beginner the label matters a little, mostly for expectations. Writing HTML is real building, and it is the fastest first win in web development, but it will not teach you how to think in logic. That kind of thinking starts with [JavaScript](/programming/javascript/). The label should never stop you from starting here, because HTML is the foundation the rest of the stack sits on. HTML browsers recover from many markup errors instead of stopping with a syntax error. That can hide a mistake until a heading appears in the wrong place or a CSS selector stops matching. Inspect the browser's document tree as well as your source. | Job | Does HTML do it? | Who does it | | Name the structure of content | Yes | HTML itself | | Set colors, spacing, and layout | No | [CSS](/programming/css/) | | Run custom application logic | No | [JavaScript](/programming/javascript/) | | Enforce rules you rely on | No | Your server | ## Who Uses HTML, and What Is It Used For? Front-end developers use HTML to build web pages. Back-end developers use it in templates, and editors use it to structure published content. Frameworks such as React and Astro also produce HTML, so the same elements matter whether you write them yourself or generate them. Inspect an element in your browser's developer tools and you will see this at work. Whatever built the page, a component library, a static site generator, or a decades-old content management system, what reaches the browser is HTML. That is how you spot a heading level that jumps from `h2` straight to `h5`, or a button that is really a styled `div` in disguise, and fix the actual problem instead of patching around it in CSS. HTML belongs alongside CSS and JavaScript in the [web programming basics](/programming/). You do not need a framework to start using it. ## Which HTML Section Should I Start With? Start with elements and document structure if you are new to HTML. Choose tables when you need to display data, or the poll project when you are ready to combine a form with server-side code. | Category | What it covers | Start here if | | [Elements & Structure](/programming/html/elements-and-structure/) | The document skeleton, headings, text, lists, links, and images | You are building your first page and need the parts in the right order | | [Tables](/programming/html/tables/) | Rows, cells, headers, captions, and spanning cells | You have real tabular data and want it readable on a phone too | | [Forms](/programming/html/forms/add-a-poll/) | Inputs, labels, and a poll form with server-side validation and storage integration | You want something on the page a visitor can actually submit | ## Why Does Semantic HTML Matter in AI-Generated Pages? Semantic HTML gives browsers and assistive technology the same useful structure whether a person or AI wrote it. Elements such as `nav` and `main` identify page regions that screen reader users can navigate. On the Turnpike you read the exit signs and ignore the storefronts along the way; landmarks are the exit signs for a screen reader. Search engines lean on the same structure, because one clear `h1` and a sensible run of `h2` and `h3` headings hand a crawler an outline it can quote. Missing image descriptions, unlabelled controls, and empty links make pages harder to use. These are worth checking in generated markup as well as code you wrote yourself. Review AI-generated markup before using it. A styled `div` can look like a button while lacking the keyboard behavior of a real button. ```html
Save
``` After styling, both lines can look like the same Save button. The first is a div the browser knows nothing about. The second is a real button, which a keyboard user can reach with Tab and activate with Space or Enter, and which a screen reader announces as a button. The div gets none of that until someone rebuilds it by hand. The HTML standard says plainly that the use of presentational elements leads to poorer accessibility. | Markup you spot | What to check | Why it matters | | A div that acts like a button | Swap in a real button | Keyboard support comes built in | | An image | Real alt text, or empty alt | Screen readers speak the alt text | | A form field | A label tied to the input | The field gets a spoken name | | A run of headings | Levels in order, one h1 | Level builds the page outline | | A link | Text that makes sense alone | Screen reader users list links | ARIA can describe accessibility information that native HTML does not provide, but it does not add keyboard behavior by itself. Start with a native element. The guides on [headings and text](/programming/html/elements-and-structure/headings-and-text/) and [links and images](/programming/html/elements-and-structure/links-and-images/) show those choices in context. ## Should I Learn HTML First or Go Straight to a Framework? Learn HTML first, and give it days rather than months. When I read the docs of the frameworks people jump to, they assume it. The Next.js getting-started page says its documentation expects you to be comfortable with HTML, CSS, JavaScript, and React. React's own docs call JSX a markup syntax that looks like HTML but is stricter, which means you write HTML-shaped code the whole time you use it. A framework does not remove HTML from the picture. It generates HTML, and the output is what browsers, search engines, and screen readers judge. If you skip the foundation, you can build a screen that looks right and still fails the people using assistive tools. | Path | What you get fast | What you risk | | HTML first, then a framework | A clear picture of the page | A slower first demo | | Framework first | Impressive screens quickly | Div soup you cannot spot | | Ask an AI to build it | A working page in minutes | Output you cannot review | Our call is to build one plain HTML bio page before you open a framework. It teaches structure, links, images, and a form with nothing in the way, and it is the first step of the [frontend learning order](/programming/topics/frontend/). The [HTML and CSS comparison](/programming/html-css/) explains why CSS comes second. If you are still deciding whether to learn to code at all, read [should you learn to code in 2026](/programming/topics/should-you-learn-to-code/) first. ## Is HTML Still Changing, and Where Did It Come From? HTML is still changing through the WHATWG Living Standard. Its roots go back to Tim Berners-Lee's work at CERN, where the early web used linked documents to share information. HTML today is a living standard, which means the WHATWG keeps updating it continuously instead of freezing numbered versions. The specification says the group chose to keep maintaining it rather than freeze it. People still say HTML5, and it simply means the modern baseline. Newer pieces such as the `dialog` element and the `popover` attribute now do jobs that once needed scripts, and the [CSS guide](/programming/css/) shows where related features stand in browsers. That history changes what you click next. A tutorial from the XHTML years teaches rules that no longer apply, and a tutorial from the table-layout years builds whole pages out of tables. The next section sorts out which advice to keep. ## Which Old HTML Advice Should I Ignore? Ignore advice to build page layouts with tables or use obsolete presentational tags such as `font` and `center`. Older tutorials can still teach useful basics, but compare their markup with current HTML practice. Tables used for page layout were common before CSS could position anything reliably, and entire sites were built as nested tables. That still parses, but it hands a screen reader a data table where there is no data. Use tables for tabular data, and use [CSS layout](/programming/css/layout/) for arranging the page. Div soup means wrapping every piece of content in a plain `div`, which was normal when the semantic elements did not exist. They do now, and [the elements guide](/programming/html/elements-and-structure/) covers which one to reach for. XHTML habits such as self-closing slashes on void elements and strictly lowercase attributes came from XHTML's XML rules. Neither is required, and neither is wrong, so leave them where you find them and do not treat them as a standard to enforce. Presentational tags have a bad reputation, yet advice that `b` and `i` are deprecated is out of date, because they were redefined rather than removed. The distinction that matters is meaning, and [the headings and text guide](/programming/html/elements-and-structure/headings-and-text/) works through it. ## Why Does a Browser Display Invalid HTML? An HTML parser does not reject your document. The HTML standard defines required processing for invalid documents as well as valid ones, so whatever you hand the browser, it builds a tree of elements and repairs the markup by fixed rules instead of stopping. Your styling and your scripts operate on the tree the browser built rather than on the file you wrote. When a CSS rule refuses to apply or a script cannot find an element, inspect the tree in developer tools before rereading your markup, because the two are not always the same document. ## Is HTML Hard to Learn, and Do AI Assistants Change That? HTML basics are approachable because you can see the result of a small edit in your browser. AI can generate those tags faster, but you still need to choose meaningful elements and check labels, links, and image descriptions. Practice with one small page before adding tables and forms. You do not need to memorize every element; learn the common ones and look up the rest when you need them. AI assistants cut the cost of typing markup to almost nothing, which moves the skill from writing HTML to judging it. I think that makes HTML more valuable to learn, since the only way to review a generated page is to read its tags. ## How Do You Structure an HTML Page? Structure an HTML page with `html`, `head`, and `body`. Inside the body, use a main heading and sections in a sensible order, with landmarks such as `nav` and `main` where they fit. Heading levels are the other half of a good outline. They form that outline, and skipping from `h2` to `h5` because the smaller size looked right breaks it. Size is a CSS decision, and level is a structural one. The [document structure guide](/programming/html/elements-and-structure/document-structure/) covers the skeleton itself, while the [elements and structure hub](/programming/html/elements-and-structure/) covers lists, links, and images, each of which carries built-in meaning that one careless habit can remove. ## How Do You Display Data in an HTML Table? Use an HTML `table` for data with meaningful rows and columns, such as prices, schedules, and specifications. Add header cells and a caption so readers can understand what each value represents. What makes a table readable is header association rather than borders. The `scope` attribute tells assistive technology which header governs which cell, so a reader hears the region and the hours instead of a bare time. The `caption` names the table for someone who cannot see where it sits on the page. Phones are where tables hurt most, since a wide table either overflows or shrinks to unreadable columns, and the [tables guide](/programming/html/tables/) covers structure, headers, captions, spanning cells, and turning a CSV into markup. ## How Do HTML Forms Collect Visitor Input? HTML forms collect visitor input through controls such as text fields and radio buttons. Labels explain each control, and a submit button sends the values to a server. The [HTML poll project](/programming/html/forms/add-a-poll/) shows the markup and the server checks needed to record a vote. ## What Should You Learn First in HTML? Learn the HTML document skeleton first, then headings, paragraphs, links, and images. Add lists, tables, and forms once you can build and read a simple page. 1. The document structure: doctype, `head`, `body`, and metadata. 2. Headings, paragraphs, lists, links, and images. 3. Tables for genuinely tabular data. 4. Forms, input types, and labels that connect to their fields. 5. Semantic elements and landmarks for structure and accessibility. 6. [CSS](/programming/css/) once the markup is solid, then JavaScript for behavior. Build a small static page once the basics feel normal. A personal profile or a recipe page teaches structure, links, images, and a form without a framework in the way. ## What Can't HTML Do? HTML cannot run your application logic or store records in a database. It does provide built-in behavior such as links, form controls, and expandable details. Use CSS for presentation and JavaScript or server-side code when you need custom behavior. It also cannot enforce any rule that you depend on. Form fields carry validation attributes and browsers honor them, but those are a convenience for the person filling in the form, never a guarantee to the server, because anyone can bypass them entirely. Validation you actually rely on happens on the server, which is why [application security](/appsec/) treats browser-side checks as a starting point rather than a defense. ## Which HTML Guide Should You Read Next? Choose an elements guide to build a page, a table guide to display structured data, or the poll tutorial to collect input. The links below follow those three tasks. ### HTML Elements and Structure Guides These guides sit under the [elements and structure hub](/programming/html/elements-and-structure/) and follow the order you build a page in. - [Document structure](/programming/html/elements-and-structure/document-structure/) covers the doctype, head, body, and the metadata a page needs before anything else. - [Headings and text](/programming/html/elements-and-structure/headings-and-text/) explains heading levels as an outline and which emphasis element carries which meaning. - [Lists](/programming/html/elements-and-structure/lists/) covers ordered, unordered, and description lists, and when each one is the better choice. - [Links and images](/programming/html/elements-and-structure/links-and-images/) covers href targets, link text that makes sense out of context, and alt text that earns its place. ### HTML Table Guides These guides sit under the [tables hub](/programming/html/tables/) and move from structure to the finer details. - [Table structure](/programming/html/tables/table-structure/) covers thead, tbody, rows, and cells. - [Headers and captions](/programming/html/tables/table-headers-and-captions/) covers the scope attribute and caption element, which help readers understand table headers and its title. - [Spanning cells](/programming/html/tables/spanning-cells/) covers colspan and rowspan without breaking header association. - [CSV to HTML table](/programming/html/tables/csv-to-html-table/) turns exported data into correct markup. ### HTML Form Project Forms have one worked guide so far, and it teaches the whole flow. - [Add a poll with HTML forms](/programming/html/forms/add-a-poll/) builds a poll form and explains server-side validation; you supply the storage functions. ## Which Guides Should You Read After HTML? HTML and [CSS](/programming/css/) are a pair, and the order you learn them in matters. HTML provides the structure and meaning, and CSS provides the presentation. Learn HTML first, because there is nothing to style until the markup exists. Then add [JavaScript](/programming/javascript/) when the page needs behavior, and reach for a framework once plain HTML, CSS, and JavaScript feel comfortable. - [HTML and CSS together](/programming/html-css/) is the comparison that explains why the pair is learned in this order. - [CSS](/programming/css/) handles styling, layout, and responsive design for your markup. - [JavaScript](/programming/javascript/) adds behavior once the structure is right. - [Frontend development](/programming/topics/frontend/) gives one learning order across HTML, CSS, JavaScript, and TypeScript. - [Programming](/programming/) is the broader language index. ## Frequently Asked Questions ### Do I need HTML knowledge when using a website builder or AI? Basic HTML knowledge helps you check pages made by a website builder or AI. You can spot missing labels, misleading headings, and links that do not explain their destination without having to write every tag yourself. ### Is HTML5 different from HTML? HTML5 names the modern version, and today the WHATWG maintains HTML as a living standard that updates continuously. You learn the current elements and check MDN for browser support when a newer one looks useful. ### When should you add ARIA attributes to HTML? Add ARIA to HTML when native elements cannot express the accessibility information you need. Prefer a real button, link, or form control when one fits, because an ARIA role alone does not add its behavior. ### Does HTML support comments? HTML supports comments between . Comments remain visible in the page source but do not appear as page content, so they are not private and should not contain secrets. ### How do HTML and XHTML differ? XHTML uses XML syntax rules, including properly nested tags and quoted attribute values. When served as XML, malformed XHTML can produce a parsing error. A document served as text/html uses HTML parsing rules even if its tags look like XHTML. ### Can you write HTML in any text editor? Yes, because HTML is plain text that any editor can save. A code editor adds syntax highlighting, tag completion, and error checking that make larger pages faster to get right, but none of that is required to produce a valid file. ### Do all browsers render HTML the same way? Browsers share HTML parsing rules, but default styles, form controls, and support for newer features can differ. Check your page in the browsers your readers use. --- ## Programming Guide: Choose a Language and Start Learning URL: /programming/ Cluster: Programming > Overview & Programming Programming means writing instructions that a computer can follow to do a job, from sorting a spreadsheet to running a website. Start with what you want to build. Python is my first choice for scripts and data work; a website starts with HTML, CSS, and JavaScript. Choose a language below, then use the [programming learning paths](/programming/topics/learning-paths/) to work through a small project. If you prefer building from prompts, start with the [vibe coding guide](/vibe-coding/); you still need to check the result before trusting it with real users or data. ## Which Programming Language Should You Learn First? Choose Python for scripts and data, JavaScript for browser interaction, or the language your intended platform already uses. HTML structures web pages and CSS styles them; both belong in a beginner web path even though they are not general-purpose programming languages. | You want to build | Start with | Why | | Your first website | [HTML](/programming/html/), [CSS](/programming/css/), and [JavaScript](/programming/javascript/) | the web still runs on them | | Automations or scripts for your own work | [Python](/programming/python/) | quick to write and runs almost anywhere | | AI, data, or machine-learning projects | [Python](/programming/python/) | widely used libraries support data analysis and model development | | A native iPhone, iPad, or Mac app | [Swift](/programming/swift/) | Apple's tools and frameworks support native app development | | A WordPress site or plugin | [PHP](/programming/php/) | WordPress core and its plugins are PHP | | A feel for the data under every app | [SQL](/programming/sql/) | query and update data in relational databases | A first project can be small. Make a page with working links, rename a folder of files, or calculate a total from a CSV file. Choose one you can check yourself. ## Pick Your Language Open the guide for the language or web technology your project needs. Learn one programming language at a time; add another when your project gives you a reason. | Language | What it is for | Choose it when | | [Python](/programming/python/) | Scripting, automation, data work, backend experiments, AI and machine-learning workflows | You want the gentlest start, or the work touches data or models | | [JavaScript](/programming/javascript/) | Behavior in the browser, and server-side work through Node | The browser is central and you need to know how pages actually run | | [TypeScript](/programming/typescript/) | JavaScript with a type system layered over it | You know JavaScript basics and want type checking. [Compare them directly](/programming/javascript-vs-typescript/) | | [HTML](/programming/html/) | Page structure, accessibility, forms, media, metadata, native browser behavior | You need to structure a web page and its forms | | [CSS](/programming/css/) | Page layout, colors, fonts, responsive design, and focus states | The structure exists and now has to look right on every screen | | [PHP](/programming/php/) | Server-rendered web apps, admin tools, WordPress, PHP and MySQL systems | You are working on WordPress or an existing PHP codebase | | [SQL](/programming/sql/) | Stored data, reporting, product analytics, imports, backend persistence | The work revolves around data that has to outlive the app | | [C#](/programming/csharp/) | Microsoft-stack services, Azure-backed systems, game backends, enterprise applications | The organization already runs on Microsoft tooling | | [Go](/programming/go/) | Backend services, command-line tools, and concurrent programs | You want small deployable binaries and few moving parts | | [Rust](/programming/rust/) | Systems-level control with memory safety enforced at compile time | Memory safety and predictable performance are the actual requirement | | [Swift](/programming/swift/) | iOS, macOS, visionOS, watchOS, tvOS, and any Apple-native app | The target is an Apple platform | An [AI coding assistant](/ai-coding-tools/) can explain an error or suggest a change while you learn. Read the code, predict its result, and check what happens. Copying a solution you cannot explain makes the next bug harder to fix. ## Which Programming Basics Carry Across Languages? Variables, conditions, loops, functions, and collections appear in many programming languages. A variable names a value, a condition chooses what happens next, and a loop repeats a task. The spelling changes, but you can reuse those ideas when learning another language. The same instruction, written twice: ```python # Python users = ["amy", "ben", "ana"] for name in users: if name.startswith("a"): print(name) ``` ```js // JavaScript const users = ["amy", "ben", "ana"]; for (const name of users) { if (name.startsWith("a")) { console.log(name); } } ``` Both examples print the names that start with a lowercase a. Python uses indentation where JavaScript uses braces, but both loop over a list and check the same condition. Learning another language also means learning its tools and environment. Browser events, database queries, and phone permissions do different jobs, even when the code uses familiar loops and functions. ## Do You Need to Learn to Code If AI Can Write It? Learning to code helps you understand, change, and debug AI-generated software. You do not need to memorize every command, but you should be able to explain the code you accept. Bobby makes the broader career case in [Should You Learn to Code in 2026?](/programming/topics/should-you-learn-to-code/). ## Four Takes on Where to Start Ask the four of us where a beginner should start and you'll get four answers, because we each came up through a different door. They share one thread. Decide what you want to build, then let the language follow. ### Start with the Web ![image]()[Matthew Lake](/about/matthew-lake/) > Start with the web, and learn security in the same breath. - The web has the fastest feedback loop there is. Change a line, save, and you see it in the browser a second later, and that is what keeps a beginner in the chair long enough to actually learn.- You can ship something real on day one, like a landing page with a working signup or a small site that wins you a first user or an interview.- Learn [Application Security](/appsec/) as you go, not as a final step. Validate what people type and escape whatever you show back, and never trust the browser as your only check. Skip that and the cost lands months later as someone else's emergency, with your name on the commit. ### Start with Python ![image]()[Caliph Herald](/about/caliph-herald/) > Python is the gentlest on-ramp, and the language for reading what the AI writes back. - It reads close to plain English, so your early energy goes into the ideas instead of fighting punctuation, and the write-it-and-run-it loop is short enough to keep you going when motivation is thin.- Python has plenty of documentation and beginner tutorials, so you have somewhere to look when you get stuck.- Plenty of people can prompt a model into producing Python. Reading the result and explaining why it works takes practice, and that is the skill I would build alongside it. ### Build the Thing, Start to Finish ![image]()[Bobby Smart](/about/bobby-smart/) > Pick a real project and take it start to finish. Scratch your own itch. - Don't start from a syllabus, start from something you actually want to exist. Look at an app you use every week and the one feature you keep wishing it had. It does not have to be big.- A course teaches the clean middle of a language. A real project drags you through the messy edges nobody puts in a lesson, like the deploy that quietly fails or the bug that only shows up on someone else's phone, and that is the part that turns you into a developer.- You will push harder on something that is yours than on any exercise set, and shipping it drills the fundamentals into you whether you set out to learn them or not. ### Don't Skip the Fundamentals ![image]()[Mark Gates](/about/mark-gates/) > Don't skip the fundamentals. AI makes them more valuable, not less. - You do not need deep computer science in your first week, but you do need to understand what is happening under the code you run, starting with what a database is and why the data outlives every other layer you build.- Frameworks and languages drift in and out of fashion, yet you can replace nearly every line of an application over its life and still be living with the database design you chose at the very start.- As more people lean entirely on the tools and skip this, the ones who understand what is happening underneath get rarer, and rare is only ever another word for valuable. We each picked the on-ramp that matches a goal. Choose the start that fits the thing you want to make, then pick up the matching track from the list above. ## What Should You Do After Choosing a Language? After choosing a programming language, learn its basic values, decisions, loops, and functions, then use them in one small project. The [step-by-step learning paths](/programming/topics/learning-paths/) connect those lessons to something you can build. For websites, follow the [frontend learning order](/programming/topics/frontend/) before adding a [backend](/programming/topics/backend/). Learn [application security basics](/appsec/) before handling accounts or private data. If you already know PHP basics, the [PHP project tutorials](/programming/php/tutorials/) cover specific features such as comments, search, and APIs. ## Frequently Asked Questions ### Which programming language should you learn first? Choose Python as a first programming language for scripts and data work, or JavaScript for interactive websites. Learn HTML and CSS alongside JavaScript for browser projects. If you are joining an existing project, start with the language that project uses. ### Do you still need to learn to code if AI can write it? Learning to code helps you check, change, and debug software generated by AI. Prompt-based builders can produce a prototype without much manual coding, but their output still needs testing and someone responsible for fixing problems. ### Should you learn HTML and CSS before JavaScript? Learn basic HTML and CSS before adding JavaScript to a web page. HTML defines the content and controls, CSS handles their appearance, and JavaScript adds behavior. You can deepen all three together as you build. ### What is the difference between a programming reference and a project tutorial? A programming reference explains a language feature or concept; a project tutorial applies several features to a specific task. Use a reference to understand a loop or function, and a tutorial when you are ready to build something with those basics. --- ## JavaScript vs TypeScript: Which Should You Learn? URL: /programming/javascript-vs-typescript/ Cluster: Programming > Overview & Programming People often talk about JavaScript and TypeScript as if they were rival languages you have to choose between. TypeScript is JavaScript with a type system bolted on top, and almost every line of JavaScript you write is already valid TypeScript. For a newcomer, the decision is when to add types. [Learn JavaScript first](/programming/), then switch on TypeScript once a project outgrows a few files or gains a second contributor. ## They Are the Same Language Underneath TypeScript is a strict superset of the JavaScript language. That means any working JavaScript file is also a working TypeScript file, and TypeScript compiles back down to plain JavaScript before it runs. Browsers still only understand JavaScript, so for the web a build step erases the type-only syntax and hands the browser ordinary code, and a few TypeScript constructs emit real JavaScript instead. Node is the exception: since v22.18 it strips erasable type syntax itself and runs a `.ts` file directly, and Node's own TypeScript documentation has marked that feature stable since v24.12. That only covers erasable syntax, though: TypeScript features that generate code, such as `enum` or constructor parameter properties, stop Node with an error and still need compiling first. Node never checks the types either, so you still run `tsc` to catch type errors. Everything you learn about variables, arrays, objects, and functions in JavaScript carries over to TypeScript without a single change. The difference is entirely about what the editor and the compiler can check before your code ever runs. ## What TypeScript Actually Adds The headline feature it adds is static types. In JavaScript a variable can hold a number on one line and a string on the next, and nothing complains until that mismatch breaks something at runtime. TypeScript lets you annotate what a value is supposed to be, and it flags the mistake while you are still typing. ```ts function total(price: number, quantity: number): number { return price * quantity; } total(10, "3"); // Error caught before running: "3" is a string, not a number ``` That same annotation powers much better editor help. Autocomplete knows the exact shape of your objects, rename-symbol works across the whole project safely, and you catch a whole class of typos and wrong-argument bugs at write time. On a large codebase with several contributors, that safety net saves hours. ## What It Costs The type system does not come for free. You add a compile step to your workflow, you spend time annotating code and occasionally arguing with the compiler about a type it cannot infer, and the setup has more moving parts than a single HTML file with a script tag. For a tiny script or a quick prototype, that overhead can outweigh the benefit. ## Side by Side | Question | JavaScript | TypeScript | | Type checking | At runtime only | Before the code runs | | Build step needed | No | For browsers, yes; Node runs erasable-only .ts files | | Editor autocomplete | Decent | Excellent, type-aware | | Setup overhead | Minimal | More configuration | | Best at small scale | Very strong | Often overkill | | Best at large scale | Gets risky | Much safer | | Where it runs | Everywhere directly | Browsers after compiling; Node runs erasable syntax directly | ## When Each One Fits Reach for plain JavaScript when you are learning the fundamentals, writing a small script, or shipping something quick where the type setup would slow you down. Reach for TypeScript once a project grows past a few files, once more than one person touches the code, or once you have been bitten enough times by a value being the wrong shape at runtime. ## The Verdict for a Beginner Learn plain JavaScript first. The types in TypeScript only make sense once you already understand the values they describe, and every concept in our [JavaScript guide](/programming/javascript/) transfers directly. Once arrays, objects, and functions feel natural, adding TypeScript is a small, high-value step rather than a second language to learn from scratch. When you are ready for it, start with the [TypeScript guide](/programming/typescript/). ## Frequently Asked Questions ### Can you use TypeScript without writing type annotations? Largely, yes. TypeScript infers types from assigned values, so a great deal of code needs no annotations at all. Function parameters and more complex shapes are where writing them explicitly earns its keep. ### Do you have to rewrite a project to adopt TypeScript? No. Adoption is file by file. A JavaScript file usually renames to TypeScript largely as-is, and the allowJs option lets both kinds live in one project while the migration proceeds at whatever pace suits. ### Does TypeScript catch every kind of bug? No. It checks the shapes and types it can see, so it will not catch a wrong business rule, a logic error, or a network call that fails at runtime. Typed code still needs tests. ### Is TypeScript owned by the same people as JavaScript? No. JavaScript is standardized by ECMA International through the TC39 committee and belongs to no single company. TypeScript is an open-source project created and maintained by Microsoft. ### Do JavaScript frameworks support TypeScript? Nearly all current major ones do, directly or through official tooling, and Angular uses it by default. Framework choice rarely rules TypeScript out anymore, which was a constraint several years ago. ## Related - [JavaScript guide](/programming/javascript/)- [TypeScript guide](/programming/typescript/) --- ## JavaScript: A Practical Guide URL: /programming/javascript/ Cluster: Programming > Overview & Programming JavaScript is the language the web runs on, and the one nearly every modern stack drops you back into sooner or later. React components, browser events, build scripts, and a lot of AI-generated code all sit on the same runtime rules. Hide those rules behind a framework and they are still there, waiting to explain your bug. If you already know what you want to learn, skip to the list of JavaScript guides. ## JavaScript at a Glance The live part of this section splits into four topics. The first is the language itself, and the other three are the places it runs into trouble. [Language Basics](/programming/javascript/language-basics/) Covers Values, functions, objects, modules Start here if The syntax itself is still new [Async](/programming/javascript/async/) Covers Promises, await, fetch, timers, races Start here if Results arrive in the wrong order [DOM](/programming/javascript/dom/) Covers Selecting, updating, events, forms, cleanup Start here if You are wiring up a real page [Node](/programming/javascript/node/) Covers The terminal, running files, npm, scripts Start here if You want to run code outside a browser ## What Is JavaScript, in Plain Words? JavaScript is a programming language that describes values and the work done on them. MDN, the web's reference library, calls it a lightweight interpreted language with first-class functions, which just means you can hand a piece of work around like any other value. A value is a piece of data: a number, a string, or an object holding named fields. A function is a named piece of work you can hand values to. An object is a bag of named values, and an array is an ordered one. Almost everything else in the language is a way of arranging those three things. The language is also loose on purpose. Nothing declares the shape of a cart item, and nothing checks it before the code runs. That looseness is why you can wire up a form in ten minutes, and it is also why a missing property, a late response, or a stale listener can reach production without anything complaining first. One more name will come up as you read around the web. ECMAScript is the standard that defines the language, and engines such as V8, SpiderMonkey, and JavaScriptCore implement it in your browser or runtime. ## What Is JavaScript Used for Beyond the Browser? The browser is the home turf for this language. JavaScript reads user input, responds to events, updates the page, calls APIs, and holds small pieces of client state, so any frontend work goes through it. MDN adds that many non-browser environments use it too, such as Node.js, Apache CouchDB, and Adobe Acrobat. Outside the browser, the host changes and the language stays familiar. | Where it runs | What gets built | Where to learn it | | Browser | Interactive pages, forms, front ends | [DOM topic](/programming/javascript/dom/) | | Node on a server | APIs and network applications | [Node topic](/programming/javascript/node/) | | Node on your machine | Scripts, automations, command-line tools | [Small automations](/programming/javascript/node/files-environment-variables-and-small-automations/) | | Desktop apps | Desktop clients such as Slack's | [Language basics](/programming/javascript/language-basics/) | | Build tooling | Bundlers, test runners, code generators | [package.json and scripts](/programming/javascript/node/package-json-npm-and-scripts/) | | Edge runtimes | Small request handlers near users | [Async topic](/programming/javascript/async/) | Slack's engineering blog is a good example of the desktop row. Its 2017 post describes the Slack desktop app as a JavaScript-based application, and explains why the team ported it to TypeScript. Node's own site describes the runtime as asynchronous and event-driven, designed to build scalable network applications. Even if you end up preferring [TypeScript](/programming/typescript/) for serious application work, the JavaScript underneath still matters. Type annotations are erased before the code runs, so closures, promises, modules, objects, and browser APIs behave exactly as this section describes. ## Who Uses JavaScript, and Is It Still Growing? It is still the most widely used language in the biggest developer survey. In the 2025 [Stack Overflow Developer Survey](https://survey.stackoverflow.co/2025/technology), 66% of all respondents and 68.8% of professional developers said they used JavaScript. Among people learning to code, the figure was 62.8%. Node.js came in at 48.7% in the same survey, ahead of React at 44.7%, and Next.js and Express sat near 20%. Python ranked fourth overall at 57.9%, and TypeScript sixth at 43.6%. GitHub's Octoverse report for 2025 adds the growth picture. JavaScript gained 427,000 contributors that year, up 24.79%, yet TypeScript grew much faster and took the top spot. GitHub also says the JavaScript and TypeScript ecosystem still accounts for more overall activity than Python alone. The two languages share one runtime, so one's rise costs the other nothing. Read those numbers as popularity and not as job offers. A language alone will not land you a role, and results vary a lot by market. ## Where Did JavaScript Come From? Early web pages were static HTML, and nothing could change a page after it loaded. JavaScript was added to the Netscape browser in 1995 so a page could run code on the visitor's machine. The language was standardized as ECMAScript, and ES2015 added the syntax most current code uses. Node later took the same language onto servers and tooling. That history matters for one practical reason. Search results do not sort themselves by age, so you will keep meeting advice written for a different era. The next section sorts the old from the new. ## Why Does Old JavaScript Advice Look Wrong? JavaScript has more outdated writing about it than almost any other language. Four relics show up most often, and knowing why saves you from copying a workaround for a problem you no longer have. - Answers built on `var`: older code declares variables in a way that ignores block scope, and `let` and `const` removed the puzzle, as the [values and equality guide](/programming/javascript/language-basics/values-variables-and-equality/) covers. - Callbacks nested into a pyramid: before promises, every step that had to wait went inside the previous one, and [promises and async/await](/programming/javascript/async/promises-and-async-await/) is where the flat modern shape is taught. - jQuery for things the browser now does: `querySelector`, `classList`, and `fetch` are built in, and [selecting elements and updating the page](/programming/javascript/dom/selecting-elements-and-updating-the-page/) shows the equivalents. - Module advice from before Node could detect ESM: current Node versions can spot ESM syntax in an ambiguous file, while explicit markers still matter, a boundary [modules, imports, and exports](/programming/javascript/language-basics/modules-imports-and-exports/) walks through. ## Is JavaScript Still Worth Learning with AI Agents Writing Code? Yes. Agents will happily draft the page, the form handler, and the fetch call, and then you are the one who has to say whether the result is any good. In the 2025 Stack Overflow survey, 84% of respondents were using or planning to use AI tools, while 46% distrust the accuracy of what those tools produce and only 33% trust it. The top frustration, cited by 66%, was answers that are almost right but not quite. Almost right is the exact shape of a JavaScript bug. An agent will write a listener that is never removed, a request whose stale answer lands after the fresh one, or a closure that captures the wrong counter. Each of those runs fine in a demo and fails later, so you only catch them if you know the language rules underneath. | Agent habit | What to check | Guide | | Fetch with no status check | Error responses treated as success | [Fetch and JSON](/programming/javascript/async/fetch-json-and-the-network/) | | Overlapping requests | Stale answers overwriting fresh ones | [Races and timers](/programming/javascript/async/timers-ordering-and-race-conditions/) | | Listeners added on every render | Leaks that grow with use | [Listener cleanup](/programming/javascript/dom/listener-cleanup-and-page-lifecycle/) | | Copying an object by assignment | Two names, one shared object | [Objects and arrays](/programming/javascript/language-basics/objects-arrays-and-destructuring/) | Tools change fast and results vary by project, but my call stays the same. Learn enough JavaScript to read what comes back, then let the assistant do the typing. For the job-market argument, our [should you learn to code](/programming/topics/should-you-learn-to-code/) article has the survey data, and the [vibe coding hub](/vibe-coding/) covers building with prompts alone. ## Should I Learn JavaScript or Python First? Start from what you want to build. If your goal has a screen on it, learn JavaScript, because it is the only language that runs natively in a browser. If your goal has a spreadsheet or a model on it, learn [Python](/programming/python/). If you just want to learn to code and cannot say more than that, my lean is Python for the gentler first week, then JavaScript the moment you want to build something people can click. | Question | JavaScript | Python | | First week project | A page that reacts to clicks | A script that renames files | | Where it runs | Every browser, plus Node | Servers and your machine | | Setup | Browser console, nothing to install | Install Python, use a terminal | | Strongest at | Web interfaces and full-stack apps | Data, scripting, AI work | | Learners using it | 62.8% in 2025 | 71.8% in 2025 | The last row comes from the Stack Overflow survey's learner numbers, and it only tells you what people picked, which is a different question from what is best for you. Both are fine first languages, and most of what you learn transfers. ## Should I Learn Frontend or Backend JavaScript? It is the same language in two different hosts, so you do not have to choose forever. Pick the one that matches the thing you want to see working first. | Question | Frontend | Backend | | Where the code runs | In the visitor's browser | On a server or your machine | | What you wrangle | Elements, clicks, forms | Files, requests, packages | | What you see first | A page changing in front of you | Text printed in a terminal | | Learn it in | [The DOM topic](/programming/javascript/dom/) | [The Node topic](/programming/javascript/node/) | If you want to see results fast, go frontend, and read the [frontend development hub](/programming/topics/frontend/) for the order to learn HTML, CSS, and JavaScript. If your goal is automating chores or building an API, start with Node. The [backend development hub](/programming/topics/backend/) also covers server languages other than JavaScript, such as [Python](/programming/python/), [Go](/programming/go/), and [PHP](/programming/php/). ## Node, Deno, or Bun? All three run JavaScript outside a browser, and all three will run your first script. Node is the long-standing choice and the one this section teaches. Deno and Bun are newer runtimes with different bets about what should come built in. | Runtime | Its own pitch | Pick it when | | Node | Asynchronous, event-driven runtime for network apps | You are learning or following guides | | Deno | Secure by default, runs .ts files directly | You want permissions and built-in tools | | Bun | All-in-one toolkit built on JavaScriptCore | One tool for runtime, installs, tests | Usage tells you where the guides and the hosting support are. In the 2025 survey Node.js was used by 48.7% of respondents, Bun by 5.5%, and Deno by 4%. Deno says its code runs in a sandbox with no file, network, or environment access until you grant it. Bun says it aims for full compatibility with Node's built-in modules, and calls that an ongoing effort. One piece of news is worth knowing. In December 2025 Anthropic announced it was acquiring Bun, and said Bun will remain open source and MIT-licensed. My advice for a beginner is still Node first, since every tutorial and error message you will search for assumes it. ## Why Does JavaScript Run One Thing at a Time? Your page or script is one agent, which means a single thread with its own queue, and one job runs to completion before the next job starts. Nothing interrupts a running function, whether it is a click, a timer, or a response from a server. Think of a diner counter in North Jersey with exactly one short-order cook. Tickets pile up on the rail, and the cook never drops a half-finished plate to grab the next ticket. A timer set to zero seconds is a ticket that still waits until the current plate is done. Work that has to wait is never done by waiting. You hand the job to the host around your code, either the browser or Node, and it hands you back a callback or a promise when the work finishes. The host drains waiting promise callbacks first and then takes one more piece of queued work, and a browser is free to repaint in between. That repeating cycle is the event loop, and Node organizes its own turns a little differently. Three consequences run through the rest of this section. Slow synchronous work blocks everything, including clicks and typing in a browser, which is why [async work](/programming/javascript/async/) exists at all. Callbacks run later than the code around them, which is why [closures](/programming/javascript/language-basics/functions-callbacks-and-closures/) matter so much in practice. And queued work has an order of its own, which [timers, ordering, and race conditions](/programming/javascript/async/timers-ordering-and-race-conditions/) works through case by case. ## How Do I Run JavaScript Code? There are two places, and which one you pick depends on what you are learning rather than on your level. In a browser, right-click any page, choose Inspect, open the Console tab, and paste the code straight in. That is the right home for anything touching a page, like elements, clicks, and forms. For a file you can save and re-run, install Node and run `node app.js` from a terminal. That is the right home for language practice and for anything with no page involved. The [Node topic](/programming/javascript/node/) starts with the terminal itself if that part is the obstacle. ## What Can't JavaScript Do? It cannot type-check your work before it runs. The engine rejects broken syntax, but nothing built in catches a misspelled property name until that line executes, which is the gap [TypeScript](/programming/typescript/) and other static analysis tools exist to fill. It cannot keep a secret in the browser. Anything shipped to a page can be read, so API keys, pricing rules, and permission checks belong on a server, a position [application security](/appsec/) starts from. It cannot reach past what its host allows. A browser page cannot silently pick files off the machine, since the file APIs need the person to choose or grant them, and Node has different limits, which is exactly why the same code does not run in both places. And one agent cannot do two things at once. Running JavaScript in parallel means a second agent, either a web worker in the browser or a worker thread in Node, and those agents can share memory rather than variables. ## Where Do I Go Next? Every Guide Under This Page Each group starts from the problem you are probably having, then lists the guides that fix it in reading order. ### Language Basics Start here if the language keeps surprising you: two values that look equal are not, or a copied object changes in two places. The [language basics topic](/programming/javascript/language-basics/) holds the four guides together. - [Values, Variables, and Equality](/programming/javascript/language-basics/values-variables-and-equality/): `let` and `const`, the primitive types, why `==` converts before comparing, and what counts as falsy. - [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/): declarations against expressions, arrow functions and `this`, callbacks, and the live variables a closure keeps hold of. - [Objects, Arrays, and Destructuring](/programming/javascript/language-basics/objects-arrays-and-destructuring/): property access, `map` and `filter` and `reduce`, spread and rest, and the difference between copying and sharing. - [Modules, Imports, and Exports](/programming/javascript/language-basics/modules-imports-and-exports/): named and default exports, import paths, the ESM and CommonJS boundary, and script loading order. ### Async Start here if everything worked until you added a request, and now results arrive in the wrong order. All three guides sit under the [async topic](/programming/javascript/async/). - [Promises and async/await](/programming/javascript/async/promises-and-async-await/): the three states a promise can be in, chaining, `await`, waiting on overlapping work, and where errors surface. - [Fetch, JSON, and the Network](/programming/javascript/async/fetch-json-and-the-network/): reading a response, `response.ok`, sending a POST, telling a bad answer from no usable answer, and cancelling with `AbortController`. - [Timers, Ordering, and Race Conditions](/programming/javascript/async/timers-ordering-and-race-conditions/): `setTimeout` and `setInterval`, what runs first, the stale-response bug, and debouncing typed input. ### The DOM Start here if a button stops working on the second click, or a page gets heavier the longer someone uses it. The [DOM topic](/programming/javascript/dom/) covers how the three guides connect. - [Selecting Elements and Updating the Page](/programming/javascript/dom/selecting-elements-and-updating-the-page/): `querySelector`, `textContent` against `innerHTML`, classes, data attributes, and building nodes. - [Events, Forms, and User Input](/programming/javascript/dom/events-forms-and-user-input/): `addEventListener`, the event object, `preventDefault`, delegation, and submitting a form with `FormData`. - [Listener Cleanup and Page Lifecycle](/programming/javascript/dom/listener-cleanup-and-page-lifecycle/): removing listeners, `AbortController` on events, when a script is allowed to run, and why leaks outlive a rewrite. ### Node Start here if the code is fine and still refuses to run: the terminal is unfamiliar, or a command is not found. The [Node topic](/programming/javascript/node/) begins with the terminal itself. - [Running JavaScript with Node](/programming/javascript/node/running-javascript-with-node/): installing and checking versions, running a file, arguments, printed output, and exit codes. - [package.json, npm, and Scripts](/programming/javascript/node/package-json-npm-and-scripts/): the fields that matter, dependencies against devDependencies, npm scripts, and what a lockfile is for. - [Files, Environment Variables, and Small Automations](/programming/javascript/node/files-environment-variables-and-small-automations/): reading and writing files, paths, environment variables, and one bulk-edit script worth stealing. ## What Should I Learn First? Learn it in roughly the order that makes real code less mysterious. 1. Start with [language basics](/programming/javascript/language-basics/): values, equality, functions, closures, objects, arrays, and modules. 2. Move to [async](/programming/javascript/async/) once ordinary code is boring: promises, `await`, fetch, and the failures each one has. 3. Add the [DOM](/programming/javascript/dom/) when you want a real page to respond to a real person. 4. Pick up [Node](/programming/javascript/node/) whenever you need to run something outside a browser, which is often sooner than people expect. 5. Build one small project end to end before going near a framework. 6. Add [TypeScript](/programming/typescript/) when a project grows contracts worth writing down. The order of the middle three is yours to choose. Someone automating a spreadsheet should go to Node third and may never need the DOM, while someone fixing a marketing site should do the reverse. Only the first step is fixed, because the other three assume it. ## What Should I Build First? Build one small feature with real inputs and real failure paths. A newsletter form is enough if you treat it seriously: validate the field, call an API, handle a failed request, disable the button while it is in flight, show a result, and take the listeners off again if the form can disappear. That one exercise touches values, closures, events, a promise, a fetch that can fail, and cleanup, which is most of what this section teaches. When it feels ordinary, a framework is the next step, and it will be a smaller step than it looks. ## Which Languages and Comparisons Sit Next to JavaScript? - [JavaScript vs TypeScript](/programming/javascript-vs-typescript/): the full side-by-side, if you are weighing whether adding types is worth it yet. - [TypeScript](/programming/typescript/): the same runtime with a type checker in front of it, for code that has outgrown being held in your head. - [HTML and CSS together](/programming/html-css/): how the two other core web languages fit with JavaScript on a page. - [HTML](/programming/html/): the document structure the DOM guides spend their time changing. - [CSS](/programming/css/): styling, layout, and states, which is often the better tool for behavior you were about to script. - [Python](/programming/python/): the stronger choice when data work and scripting matter more than running in a browser. - [Programming](/programming/): the wider language index, including the data and database guides. ## Frequently Asked Questions ### Which topic should you start with? Language basics, unless you already write JavaScript comfortably. Async is the topic most people are stuck in, but promises are hard to reason about before closures make sense. The DOM and Node topics are chosen by where your code runs rather than by order. ### What is the difference between JavaScript and Java? They are unrelated languages that share part of a name for historical marketing reasons. Java is a statically typed language that usually runs on the JVM; JavaScript is dynamically typed and runs in browsers and in runtimes such as Node. ### What is the difference between JavaScript and ECMAScript? ECMAScript is the standard that defines JavaScript, and engines such as V8, SpiderMonkey, and JavaScriptCore implement it. Names such as ES2015 refer to editions of that standard, while whether a feature works still depends on the browser or runtime version in front of you. ### Do you need a framework to learn JavaScript? No, and starting with one usually slows you down. The DOM, events, and asynchronous behavior are the foundation every framework sits on. Learn those first, then a framework makes sense because you can see what it is doing for you. ### Do you need Node to learn JavaScript? Not for browser work, where the console in your developer tools runs code immediately. Reach for Node once you want to run a file on your own machine, script outside a page, or use the tooling most projects are built with. Deno and Bun run JavaScript files too; this section teaches Node. ### Is JavaScript single threaded? One agent, meaning one page or one script, runs its jobs on a single thread and finishes each one before starting the next. Workers are separate agents with their own threads, and agents can share memory, so JavaScript as a whole can use more than one thread. ### Should you learn TypeScript as well? Eventually, once JavaScript itself stops being the confusing part. TypeScript checks the shapes your code expects, and its type-only syntax is erased before execution while a few constructs emit JavaScript. Either way, every runtime rule on this page still applies underneath it. ### Should you learn JavaScript or Python first? Pick by what you want to build. JavaScript is the only language that runs natively in a browser, so it wins for anything on the web, while Python is stronger for data, scripting, and automation. Both are reasonable first languages for a total beginner. ### Is JavaScript still worth learning now that AI writes code? Yes, it is still worth learning for most people. Assistants draft JavaScript quickly, and in the 2025 Stack Overflow survey the biggest frustration with AI output was answers that are almost right but not quite. Knowing the language is how you spot the stale response, the leaked listener, or the shared object before a user does. ### Is JavaScript hard to learn? The basics are approachable, and a browser console gives you feedback with nothing to install. The hard parts arrive later: equality rules, closures, and asynchronous code. Most beginners hit those within the first few weeks, which is normal and says nothing about your ability. ## Where to Start Open your browser console, paste in a one-line experiment, and see what happens today. Then work through [language basics](/programming/javascript/language-basics/), and read [the DOM guides](/programming/javascript/dom/) alongside your first small build rather than before it. --- ## PHP Guide: Server-Side Web, Tutorials, OOP, and Frameworks URL: /programming/php/ Cluster: Programming > Overview & Programming PHP is the [language](/programming/) behind WordPress, Laravel, and a large share of the working web. Start with PHP if you want to build server-rendered websites, work on WordPress, or create a database-backed application. Learn request handling, forms, and safe database queries before moving into a framework. ## What Is PHP? PHP is a programming language commonly used on servers to generate web pages and API responses. It also runs command-line scripts. The PHP manual describes it as a widely used open source scripting language that is especially suited to web development. The browser sends a request, the server hands it to PHP, PHP runs your code, and the browser receives the finished HTML or JSON that comes back. A request is one visit to one address, such as a page load or an API call. A server-side language is one that runs on the machine hosting the site and never inside the visitor's browser. A framework is a bundle of ready-made code, such as Laravel, that handles the repeated parts of an application so each project starts further along. PHP applications often store records in a database such as MySQL or MariaDB, although they can also read files and caches. The language is free to use, and its source repository states that PHP is distributed under the Modified BSD License. That matters to anyone who prefers to read what they run. ## Is PHP Still Worth Learning in 2026? PHP is worth learning for WordPress development, Laravel applications, and database-backed websites. The Stack Overflow Developer Survey put PHP at 18.58 percent of all respondents in 2023, 18.2 percent in 2024, and 18.9 percent in 2025. Those surveys show similar usage among their respondents; they do not measure every PHP project. PHP is the steady option in that survey, while Python gained about seven points in the same year. The language itself is actively maintained by a funded group of contributors. The PHP Foundation, a collective of people and organisations that rely on PHP, funds developers who work on its maintenance, security, and new features. PHP 8.5 shipped on 20 November 2025, and php.net lists the 8.2 through 8.5 branches as supported today. Each release gets two years of active support and two more years of security fixes, which gives a new project a long runway. | Your goal | Does PHP fit? | Why | | Build or extend a WordPress site | Yes | WordPress is built on PHP, so themes and plugins are PHP | | A product with accounts, billing, and background jobs | Yes | Laravel and Symfony cover the repeated work | | A small database-backed site on ordinary hosting | Yes | No build step, and a mature LAMP deployment path | | Machine learning or data analysis | Rarely | Python has the larger ecosystem and the faster growth | | A mobile app | No | PHP runs on servers, so a phone needs another language | | Thousands of long-lived open connections | Awkwardly | A web request ends after the response; long work belongs in workers | What I like about PHP is how little ceremony stands between a form and a stored row. I ran PHP and MySQL on a LAMP server that I administered myself through the dot-com years, and that directness is why the language spread. The cost is thirty years of tutorials that nobody sorted by age, which the security section below deals with. Read the [2025 survey tables](https://survey.stackoverflow.co/2025/technology) yourself before relying on my summary of them. ## What Runs on PHP, and Who Uses It? WordPress describes itself as built on PHP and MariaDB and licensed under the GPLv2, and it states that it powers over 43 percent of all sites across the web. Its requirements page recommends PHP 8.3 or greater and says that versions as old as 7.4 still work but have reached end of life. A newcomer who learns PHP can therefore read, fix, and extend the software that a large part of the web runs on. Wikipedia is another large example of PHP in production. The MediaWiki manual says the software uses PHP to process and display data stored in a database, and it names Wikipedia as the site MediaWiki was built for. Facebook is a third, with a twist. Meta's engineering blog explained in 2014 that the company had migrated nearly its entire PHP codebase to Hack, a language it built to join the fast development cycle of PHP with static typing. A company that outgrew plain PHP kept the fast development cycle and added types, which tells a beginner how durable the model is. Laravel is a common choice for new PHP web applications. Its documentation describes it as a framework for building modern web apps and AI agents. In the 2025 Stack Overflow survey WordPress was used by 13.6 percent of all respondents and Laravel by 8.9 percent, so both are widely used and neither is a niche tool. | What runs on PHP | What it is | Why people choose it | | WordPress | A content management system | Publishing without building the admin screens | | Laravel | An application framework | Accounts, queues, and billing already solved | | MediaWiki | A wiki engine | It was built for Wikipedia's scale | | Plain PHP | Scripts, forms, and small sites | The fewest moving parts | ## Where Did PHP Come From, and Is It Growing? Rasmus Lerdorf created PHP in 1994 as a set of small CGI programs, called Personal Home Page Tools, that tracked visits to his online resume. Andi Gutmans and Zeev Suraski rewrote the parser in 1997, and the 1998 release became PHP: Hypertext Preprocessor. The php.net history page records about 60,000 domains using PHP by May 1998, roughly one percent of the Internet at the time. The Zend Engine arrived with PHP 4 in 2000, PHP 5 followed in 2004, PHP 7 in 2015, and PHP 8 in 2020. Each step added speed or features while keeping the same model of one request in and one response out. Zeev Suraski tells the engine story in his own words in [the interview on PHP's engine history](/interviews/01-zeev-suraski-php/). The history matters for one practical reason. Search results still rank tutorials from the PHP 4 and PHP 5 era beside current ones, and some of that advice is unsafe today. The [old advice section](#why-old-php-advice-is-dangerous) below lists the shapes to recognise. On growth, the survey line is flat, as the worth-learning section showed, but those results do not predict future adoption. ## Which PHP Version Should a New Project Use? A new PHP project should use the newest supported PHP branch compatible with its host, framework, and plugins. As of 20 October 2026 the supported branches are the four below, and WordPress recommends PHP 8.3 or greater. | Branch | Active support ends | Security fixes end | | PHP 8.5, released 20 November 2025 | 31 December 2027 | 31 December 2029 | | PHP 8.4, released 21 November 2024 | 31 December 2026 | 31 December 2028 | | PHP 8.3, released 23 November 2023 | Ended 31 December 2025 | 31 December 2027 | | PHP 8.2, released 8 December 2022 | Ended 31 December 2024 | 31 December 2026 | My rule is to start new work on 8.5, accept 8.4 where a dependency has not caught up, keep 8.3 for applications already running on it, and treat 8.2 as a migration job with a deadline. Versions 8.0 and 8.1 are already marked end of life on php.net. An old tutorial that mentions them tells you its age, and the [old advice section](#why-old-php-advice-is-dangerous) explains what else to distrust in it. ## Why Does PHP Forget Everything Between Page Loads? In a typical PHP-FPM application, each web request starts with fresh script variables. Values that must survive another page load belong in a session, database, cache, or file. Long-running PHP application servers can behave differently. ```php Overview & Programming Python is a general-purpose [programming language](/programming/) that reads close to plain English, which is a big reason beginners get pointed at it first. You write instructions into a text file, run the file, and Python carries them out with no separate build step in between. ## Python at a Glance Python lists keep items in order. Dictionaries pair keys with values, such as a username and an email address. Once you understand loops, comprehensions give you a shorter way to build lists and dictionaries. | Topic | What it covers | Start here if | | [Lists](/programming/python/lists/) | Ordered items, slicing, copying | You are brand new to Python | | [Dictionaries](/programming/python/dictionaries/) | Values stored under names, JSON | Numbered positions stop making sense | | [Comprehensions](/programming/python/comprehensions/) | Building collections in one line | You can write a loop already | ## What Is Python? Python is a programming language, which means it gives you a fixed vocabulary and a set of rules for telling a computer what to do. It is general-purpose, so the same language handles a ten-line script that renames your files and the back end behind a busy website. You normally run a Python file without a separate compile command. The interpreter handles the steps needed to execute it. Save a change, run the file again, and see what happened. That short feedback loop helps when you are learning. The language also cares how your code sits on the page. Where many languages use curly braces to show what belongs inside a loop, Python uses the indentation you were going to write anyway. Then there is the standard library, which ships with Python and already covers a lot of ordinary work: reading files, parsing dates, fetching a web page, handling JSON. A surprising number of beginner projects (a file tidier, say, or a report builder) never need anything beyond it. You also do not declare types up front. A variable holds whatever you assign to it, and Python checks at the moment the code runs. Type hints exist for people who want stricter contracts later, and they stay optional. ## What Is Python Used For? Python is used for automation, web back ends, data analysis, machine learning, and software testing. You might use it to rename files, build an API, or turn a spreadsheet into a chart. | Area | Typical Project | Common Tools | | Automation | Renaming or moving files | Standard library only | | Web back ends | Server-side apps and APIs | Django, FastAPI, Flask | | Data analysis | Cleaning and charting data | pandas, Matplotlib | | Machine learning | Training and running models | PyTorch, scikit-learn | | Testing | Automated checks on code | pytest | | Teaching | University intro courses | IDLE, Jupyter | Libraries let you tackle jobs beyond the language itself. pandas handles tables of data, while PyTorch provides tools for machine learning. You still need to understand the problem; installing a library does not do that part for you. Automation is the quieter half, and it is where plenty of beginners get hooked. The first time a fifteen-line script renames two hundred files correctly, the language starts to feel like a tool you own. For [backend development](/programming/topics/backend/), Django and FastAPI are names you will keep meeting. Lists and dictionaries help you handle the records and responses moving through those apps. ## Who Uses Python, and Is It Still Growing? Beginners, web developers, data analysts, and researchers use Python. Its adoption rose by seven percentage points between the 2024 and 2025 [Stack Overflow Developer Surveys](https://survey.stackoverflow.co/2025/technology). That is evidence of growth among survey respondents, rather than a count of every programmer. Popularity makes it easier to find libraries, tutorials, and people who have hit the same error. It does not make Python the best language for every project. Choose it because it fits the work you want to do. Company posts show how big teams lean on it. Dropbox wrote in 2019 that Python is its most widely used language for both backend services and the desktop client, and that its largest backend repository had grown to about 4 million lines of type-checked code. Meta's engineering blog says Instagram runs one of the world's largest deployments of the Django web framework. Those are large codebases, but their scale is not a reason to start with their architecture. A small script is a better first project than a miniature Instagram. ## Where Did Python Come From? Guido van Rossum started Python in December 1989 at CWI, the national research institute for mathematics and computer science in Amsterdam. He had spent years on a teaching language called ABC, which was readable and pleasant but awkward to extend. Python kept the readability and dropped the parts that made ABC a dead end for real work. He posted version 0.9.0 to the alt.sources newsgroup in February 1991, and Python 1.0 followed in January 1994. Python 2.0 arrived in October 2000 and introduced list comprehensions, which you will meet again in the [comprehensions guide](/programming/python/comprehensions/). Python 3.0 shipped in December 2008 and deliberately broke compatibility with Python 2 so the language could fix how it handled text and bytes. That migration took over a decade, and Python 2 reached end of life on January 1, 2020. Van Rossum led the project as its "benevolent dictator for life" until July 2018, and since 2019 an elected five-member Steering Council has made the final calls under PEP 13. The name has nothing to do with snakes. According to the official Python FAQ, van Rossum was reading published scripts from Monty Python's Flying Circus and wanted a short, unique, slightly mysterious name. This history changes what you do next for one practical reason. A tutorial written for Python 2 may show code that fails under Python 3, and the clearest giveaway is `print` without parentheses. Close that tab and find a newer one. ## Is Python Easy to Learn? Python is approachable for beginners because its syntax is readable and you can try one instruction at a time in the interactive interpreter. Learning the basics is only the start. Writing programs you can explain and fix takes practice. That gentle start still has rough edges. Indentation mistakes can stop a script from running, and a package installed for one Python interpreter may be missing from another. Mutable default values and shallow copies can also produce surprises once your programs get larger. Most of what people write needs little math. Data science and machine learning are the exception, because the ideas underneath those libraries are statistical. Learn the traps from the guides and not from a production bug. The [lists guide](/programming/python/lists/) covers the copying rule, and the [dictionaries guide](/programming/python/dictionaries/) covers missing keys. ## Should I Learn Python or JavaScript First? Learn Python first for automation, data analysis, or machine learning. Learn [JavaScript first](/programming/javascript/) for interactive web pages. When you cannot say more than "I want to learn to code," my pick is Python, since the first week is lighter. | Question | Python | JavaScript | | How the code looks | Close to plain English | More braces and punctuation | | Best first win | A script that does a chore | A page that reacts to you | | Main trade-off | Not the usual browser language | Fewer data and AI libraries | | Types | Optional hints | Dynamic; TypeScript adds checks | Variables, loops, functions, and debugging transfer between languages, so the first choice does not lock you in. If you later want a type checker for JavaScript, [TypeScript](/programming/typescript/) adds one. ## Is Python Slow, and Does It Matter? Pure Python loops can run slower than equivalent compiled code, and for most beginner work that rarely matters. Think of the New Jersey Turnpike: a trip is usually decided at the toll plaza and not under the hood, and a script that waits on files, networks, and databases behaves the same way. Faster arithmetic would not shorten the wait. | Job | Does speed matter? | Why | | Renaming files, calling APIs | Rarely | The wait is disk and network | | A web API | Sometimes | Databases usually dominate | | Heavy number crunching | Yes | Compiled library operations can help | | Real-time games or engines | Yes | Every frame is on the clock | Numerical libraries such as NumPy move bulk work into compiled code. Measure where your program spends its time before rewriting it in another language. A faster loop will not fix a slow database query. If profiling shows that Python execution is the bottleneck, [Go](/programming/go/) or [Rust](/programming/rust/) may be worth considering. For a first automation script, I would still start with Python and measure before reaching for another language. ## How Do AI Agents Change Learning Python? AI agents can draft Python code, but learning Python still matters because you need to check whether that code does the right thing. Read the result, try a small input, and check what happens when the input is missing or wrong. My take is that Python is the gentlest on-ramp, and the language for reading what the AI writes back. The syntax is short enough to review line by line, and the few traps are well known. These are useful patterns to check in generated code. | Code pattern | What to check | Guide | | Copying a list with a slice | Inner items still shared | [Lists](/programming/python/lists/) | | Calling get() on every lookup | Missing data quietly turns into None | [Dictionaries](/programming/python/dictionaries/) | | Nesting comprehensions deeply | Can you trace it in a minute? | [Comprehensions](/programming/python/comprehensions/) | | Repeated membership checks on hashable items | A set can avoid repeated list scans | [Lists vs dictionaries vs sets](/programming/python/lists/lists-vs-dictionaries-vs-sets/) | Let the assistant draft, then read it the way you would read a friend's pull request. For which assistants to try, the [AI coding tools hub](/ai-coding-tools/) has the current picture, and our [should you learn to code](/programming/topics/should-you-learn-to-code/) article covers the job-market argument. ## When Should You Choose Python Over Another Language? Python is the safe default for scripts, data, and AI, and other languages beat it on specific jobs. Use the table to match the job to the language. | Job | Strong pick | Why | | Scripts and automation | Python | Quick to write, runs almost anywhere | | Data or machine learning | Python | The tooling is Python-first | | Web back end | Python or Go | Django and FastAPI, or simple deploys | | Browser interface | JavaScript | Browsers run it natively | | First iPhone or Android app | Swift or Kotlin | Python on phones stays niche | | Memory-critical systems code | Rust | Control without a garbage collector | For the sibling languages, see [JavaScript](/programming/javascript/) and [TypeScript](/programming/typescript/) for the browser side, [Go](/programming/go/) and [Rust](/programming/rust/) for speed, and [Swift](/programming/swift/) for Apple apps. ## What Should You Learn First in Python? Learn Python variables, basic types, conditions, and loops first. Then use lists and dictionaries to work with more than one item, followed by functions to organize reusable code. You can practice in an interactive interpreter before writing longer scripts. 1. Variables and the basic types: text, whole numbers, decimals, true or false values. 2. Printing output and reading input, so a script can hold a conversation with you. 3. Conditions with `if` and `else`, then loops with `for` and `while`. 4. Lists, which keep items in the order you put them. 5. Dictionaries, which store values under names instead of numbered positions. 6. Functions, so you can name a block of work and reuse it later. 7. Reading error messages properly, because you are going to see plenty of them. 8. Files, then virtual environments and pip once a project needs an outside package. Leave classes, decorators, and async alone until that list feels ordinary. They solve problems you have not hit yet, and reaching for them early is how beginners end up with code they cannot debug. Write something small and real at that point, because a script that tidies your downloads folder will teach you more than another hour of tutorial video. ## What Is Python Not the Right Tool For? Python is not my first choice for browser interfaces, native phone apps, or programs that need tight control over memory and execution speed. JavaScript is the usual choice for interactive web pages. Python can run in a browser through projects such as Pyodide, but that is extra setup for a beginner. Mobile apps are another gap in the language's reach. Native iOS and Android work goes to Swift and Kotlin, and the Python options for phones stay niche enough that I would not start a first app there. For performance-sensitive work, check the actual bottleneck before switching languages. For package conflicts, use a separate virtual environment for each project. Those are different problems with different fixes. ## Which Python Guide Should You Read Next? Read the Python lists guide first if you are new to collections, then dictionaries for named values. Use the method guides when you need to add or remove an item, and move to comprehensions after ordinary loops make sense. - [Python Lists](/programming/python/lists/): the ordered collection you reach for first, covering indexing and slicing, adding and removing items, sorting, the copying rule that surprises everyone once, and the jobs a list is wrong for. - [Python List append()](/programming/python/lists/append/): adding one item to the end of a list, what happens when you append a list, and how it compares with extend() and +=. - [Python List insert()](/programming/python/lists/insert/): adding one item at a chosen position, how negative and out-of-range indexes behave, and when append() or a deque fits better. - [Python List remove() vs pop()](/programming/python/lists/remove-and-pop/): deleting by value against deleting by position, what each one returns, and the errors each can raise. - [Python Dictionaries](/programming/python/dictionaries/): storing values under names instead of positions, with safe key access, live views, the counting and grouping patterns, and where dictionaries meet JSON. - [Lists vs Dictionaries vs Sets in Python](/programming/python/lists/lists-vs-dictionaries-vs-sets/): choose a collection based on order, duplicates, lookup cost, and the values you need to store. - [List and Dict Comprehensions in Python](/programming/python/comprehensions/): building a list or a dictionary from another iterable in one expression, plus the cases where a plain loop reads better. Put what you learn into practice with a small script that does a real chore, such as sorting your downloads folder into subfolders. Save your script in a file ending in `.py`. Leave the collection comparison until you have used a list and a dictionary in that program. For a longer sequence of projects, the [programming learning paths](/programming/topics/learning-paths/) give you a way to keep practicing. ## Frequently Asked Questions ### Do you need to be good at math to learn Python? Learning Python does not require advanced math for everyday tasks such as renaming files or processing text. Logic and careful reading matter more at the start. Statistics and other mathematical topics become important if you move into data science or machine learning. ### How long does it take to learn Python? The time needed to learn Python depends on your starting point, practice time, and goal. Writing a small script is a much smaller task than maintaining an unfamiliar application. Aim first to write, explain, and fix a useful script without copying every line from a tutorial. ### Do you need to install anything before writing Python? You do not need a local Python installation to try code in an online Python environment. Install Python 3 when you want to run scripts on your own computer. For an existing project, use the Python version that the project supports. ### What is the difference between Python 2 and Python 3? Python 3 is the maintained series; Python 2 reached end of life on January 1, 2020. Python 3 changed parts of the language, including text handling and print syntax, so old Python 2 examples may not run unchanged. Use Python 3 for new work. ### Do you need a virtual environment for every Python project? A Python project that uses third-party packages should usually have its own virtual environment so its packages stay separate from other projects. A simple script using only the standard library does not need one. Create an environment with python -m venv .venv, then activate it before installing packages. ### How do you install a third-party Python package? Install a third-party Python package with python -m pip install followed by its package name, inside an activated virtual environment. Record the dependencies in a requirements file or pyproject.toml so someone else can install what the project needs. --- ## Rust Guide: Memory Safety, Tooling, and Systems Performance URL: /programming/rust/ Cluster: Programming > Overview & Programming The compiler is going to argue with you, and a lot of the time it is going to be right. That can be annoying when you are learning the language. It is also the whole reason people put up with the learning curve. Rust moves many memory, ownership, and state mistakes from "maybe production will tell us" into "the compiler will not let this build." Rust is a systems programming language built around explicit ownership, memory safety, and predictable performance. It gives you low-level control without a garbage collector. The tradeoff is that the language asks you to say who owns a value, who can borrow it, what errors can happen, and what states are allowed. This Rust track gives developers practical explanations. You should come away knowing why the compiler objected, what design choice it is asking you to make, and how to write code that is safe without turning every function signature into a small legal document. ## What Is Rust, in Plain Words? Rust is a language for software that sits close to the machine, such as servers, command-line tools and pieces of operating systems. The Rust Core Team announced version 1.0 on 15 May 2015 and described a language with low-level control, high-level convenience and safety guarantees, achieved without a garbage collector or runtime. The Rust home page today pitches three things: performance, reliability and productivity. Picture a building inspector who signs off before anyone pours the concrete. A city that only inspects after the roof caves in learns the lesson the expensive way. In Rust, the compiler is that early inspector, checking how your program uses memory and threads before it ever runs. It refuses to build code that breaks the rules. Four terms come up everywhere on this page: - Compiler: the program that turns your Rust code into a program the computer can run, and that explains what it dislikes. - Ownership: the rule that every value has one owner, and the value is cleaned up when that owner goes away. - Crate: one unit of Rust code, either your own program or a library someone published. - Cargo: Rust's build tool and package manager, which runs your code and fetches crates. Nothing below assumes you know another language, though the compiler will make more sense if you have written a little code somewhere. ## Where Rust Fits Rust is strongest when correctness, performance, and resource control all matter at the same time. That includes command-line tools, infrastructure services, parsers, compilers, embedded systems, edge components, performance-sensitive libraries, and backend services where predictable memory use matters. It is also useful when you are replacing unsafe or fragile low-level code. Rust will not make the design good automatically, but it removes whole classes of memory bugs when you stay inside safe Rust. Buffer overflows, use-after-free bugs, dangling references, and data races become much harder to ship. If you need a small script, [Python](/programming/python/) will usually get you there faster. If you need a straightforward operational service, [Go](/programming/go/) may feel lighter. Rust earns its keep when you want the machine-level control and you are willing to make the invariants explicit. ### Who Should Learn Rust? The Rust project interviewed developers for its Vision Doc work and wrote up what it heard about learning in June 2026. One theme stood out: the hard part for experienced programmers was unlearning habits from other languages, and syntax barely came up. That finding shapes who the language suits best: - New to programming: possible, and one interviewee reported that a person with little prior programming got going fine. That is a single story, so treat it as a lead. - Coming from Python or JavaScript: you gain speed and compile-time checks, and you take on ownership, which has no equivalent in either language. - Coming from C or C++: ownership replaces manual freeing, and the object-oriented habits are the part to unlearn. - Doing data science or machine learning: stay with Python, and use Rust for the fast parts underneath if you ever need to. ## What Do People Build with Rust, and Who Uses It? The Rust home page lists four areas: command-line tools, WebAssembly, network services and embedded devices. Real companies have gone further than that list, and this table shows what each one said in its own words. The dates matter, because some of these posts are several years old. | Who | Used Rust for | Source and year | | Google (Android) | New low-level platform code | Security blog, 2025 | | AWS | Firecracker, S3 and EC2 services | AWS blog, 2020 | | Cloudflare | Pingora proxy | Company blog, 2022 | | Discord | Read States service | Company blog, 2020 | | Dropbox | File sync engine | Company blog, 2020 | | OpenAI | Codex command-line tool | GitHub discussion, 2025 | | Linux kernel | Rust support and documentation | Kernel docs, checked 2026 | Google reported in November 2025 that Android had roughly five million lines of Rust. By its estimate, memory safety vulnerability density in that code is more than 1000 times lower than in Android's C and C++, and larger Rust changes had a rollback rate about four times lower. Cloudflare wrote in 2022 that its Rust proxy, Pingora, serves over one trillion requests a day on a third of the CPU and memory of the proxy it replaced. AWS wrote in 2020 that Firecracker, the technology behind Lambda, is built in Rust, and that S3, EC2 and CloudFront services also use it. Dropbox wrote in 2020 that Rust was a force multiplier for its sync engine because its focus on correctness helped tame complexity. The Linux kernel's own documentation now has a Rust section with a quick start guide. The reasons repeat: memory safety without a collector, speed you can predict, and fewer production surprises. They are good reasons, and they only matter when your project needs them. ## How Did Rust Start, and Where Is It Now? The history matters here because it explains why old advice looks odd. - Rust 1.0 shipped on 15 May 2015 with a promise that code written for it keeps working, which the Edition Guide calls stability without stagnation. A new release has arrived every six weeks since, so articles go stale quickly. - Editions are opt-in bundles of language changes. Rust 2018 arrived with Rust 1.31 and Rust 2021 with 1.56. A tutorial full of `extern crate` lines comes from before 2018. - Rust 2024 became stable with Rust 1.85 on 20 February 2025, alongside async closures. New projects from `cargo new` use it by default. - Rust 1.88 (June 2025) added let chains, which only work in the 2024 edition. Rust 1.95 (April 2026) added `if let` guards in match arms. - Rust 1.99 arrived on 1 October 2026 and is the current stable release. ## Is Rust Popular, and Is It Growing? Yes, by the broadest measure. The Stack Overflow Developer Survey asked the same question in 2024 and 2025, and every language below gained ground among respondents. | Language | Used in 2025 | Used in 2024 | | Python | 57.9% | 51% | | TypeScript | 43.6% | 38.5% | | Go | 16.4% | 13.5% | | Rust | 14.8% | 12.6% | That table covers survey respondents only, and the respondent mix changes from year to year. On a different measure the 2025 survey called Rust the most admired language yet again, at 72%, meaning people who used it want to keep using it. It also named Cargo the most admired tool in its cloud and infrastructure group, at 71%, and the Rust web framework Axum the second most admired web framework, at 76%. The Rust project's own 2025 survey drew 7,156 completed responses and described a hiring trend of organizations looking for more Rust developers. It also recorded worries that the language keeps getting more complex, and slow compile times and storage use remain among the most reported problems. So Rust is growing and well liked, and it is also demanding. ## Is Rust Hard to Learn, and Do Agents Change That? It is demanding at the start, because ownership feels strange the first time. The Rust Vision Doc post on learning, published in June 2026, quotes one learner who read the Rust Book's borrowing chapter twice and then watched a video. Several said it only clicked after they wrote a lot of Rust, and one engineer said the stack of books and exercises they used was "not a one-pass reading." Experienced Rust developers gave that post the same advice again and again: clone freely while you are learning, and optimize later. One researcher described weaving lifetimes through a whole project to avoid a single clone, and then watched someone else clone the struct without any harm. The post also found the compiler's messages taught several people about lifetimes before any documentation did. On AI agents, the same post says interviewees used AI chat tools as a search engine and an example generator, and the Rust team writes that it does not yet know what to make of them as a learning tool. The 2025 Stack Overflow survey reported 84% of respondents using or planning to use AI tools, with 46% distrusting the accuracy and 33% trusting it. So the skill that pays off is reading. Let an assistant draft, then check how it handles ownership and every `Result`. The compiler acts as a second reviewer that rejects whole classes of mistakes, and that is why Rust suits agent-written code. It cannot tell you whether the design is right, so that part stays with you. ## Should I Use Rust or Go for This Project? Ask what the project will punish you for. A service that must ship this quarter, read by a whole team, points to Go. A program where memory use or response time has to stay predictable, or where the code sits close to the operating system, points to Rust, and it is worth the extra learning time. | You care about | Rust | Go | | Time to first service | Slower, ownership comes first | Quick, small language | | Memory and latency | You decide, with no collector | Collector decides, simpler code | | Threads and async | Powerful, with a steeper climb | Goroutines built in | | Team onboarding | Stricter, more concepts | Gentler, fewer concepts | | Deploying | One program file | One program file | OpenAI is a recent example of choosing Rust on purpose. It announced in May 2025 that it was rewriting its Codex command-line tool from TypeScript into Rust, and it cited an install with no Node dependency, native security bindings, no garbage collector and an extensible protocol. GitHub's language statistics now list Rust as roughly 97% of that repository's code. Go has plenty of loyal users as well, since the Go team's 2025 survey reported 91% of respondents satisfied with it. Discord wrote in 2020 that it moved one latency-sensitive service from Go to Rust because of garbage collection pauses, and that post predates Go's newer collector, so measure your own service before you rewrite anything. ## Can I Use Rust on the Backend? You can, and plenty of companies do. The Rust home page lists network services as a core area, and Cloudflare, Discord and AWS each describe production services written in it. Axum, a web framework from the Tokio project, was the second most admired web framework in the 2025 Stack Overflow survey. Nothing about the language stops you from building an API. The question is whether you should, and that depends on the job. Async Rust is the hardest corner of the language, and the Rust project's interviews found that beginners often ignore it while learning. Compile times and onboarding cost are real, too. A team that knows [TypeScript](/programming/typescript/), Python or Go can ship a plain forms-and-database backend faster in those languages. DHH, the creator of Ruby on Rails, makes a pragmatic version of this point on his own blog. In 2023 he wrote that when one screen sees wild load spikes and cannot be cached, you might "pull out Rust or Go or whatever to squeeze all the juice out of your CPU." In 2024 he wrote that he would not be a happy camper programming Rust all day, though he loves many tools that community makes. That is one developer's taste, and it points to a sound habit: keep most of the app in a productive language, and reach for Rust at the hot spot. ## Is Rust Only for CLI Tools? No, though command-line tools are a sweet spot. The Rust site lists four areas, and the project's users run well beyond them: Android's platform code, a sync engine at Dropbox, AWS virtualization, and the Linux kernel's documented Rust support. Codex CLI is the best-known recent command-line example, and it moved to Rust partly because Rust builds a program with nothing else to install. Rust also compiles to WebAssembly, which lets you run it inside a web page, and it reaches down to embedded devices. Learners called the embedded route harder in the Rust project's own interviews, so treat it as a later step. ## Rust, Go, Python or TypeScript for Common Jobs? This table is my call for a beginner choosing a first tool for a job, based on what the official sources say each language is for. Plenty of teams mix them, and no single language lands anyone a job. | Job | Start with | Why | | Command-line tool to share | Rust or Go | One file, no runtime to install | | Performance-critical core | Rust | No collector, tight control | | Embedded or low-level code | Rust | Runs without a runtime | | JSON API for a product | TypeScript, Go or Rust | Match your team's skills | | Data analysis and AI work | Python | That is where the libraries live | | Website front end | TypeScript or JavaScript | Browsers run them natively | | Quick automation script | Python | Fastest from idea to result | | First phone app | Swift or Kotlin | Native to the platform | Mixing languages is normal on real teams. A Rust service often sits behind a TypeScript front end, and a Python script often prepares the data it reads. Pick the language that fits the job in front of you, and let the next job pick again. ## What Rule Does the Rest of Rust Depend On? Every value in Rust has exactly one owner, and most of the rest of Rust leans on that. The Rust Book says the value is dropped when the owner goes out of scope. The program below gives a piece of text a second name and then tries to use the first one. ```rust fn main() { let title = String::from("Quarterly report"); let backup = title; println!("{title} and {backup}"); } ``` ```text # Compiler output (trimmed): error[E0382]: borrow of moved value: `title` --> src/main.rs:4:16 | 2 | let title = String::from("Quarterly report"); | ----- move occurs because `title` has type `String`, which does not implement the `Copy` trait 3 | let backup = title; | ----- value moved here 4 | println!("{title} and {backup}"); | ^^^^^ value borrowed here after move ``` Handing the text to `backup` moved ownership, so `title` stopped working. The compiler points at each step and even suggests copying the value instead. Borrowing, lifetimes and smart pointers are all answers to the same question: how do two parts of a program use one value safely? Learning to answer that question is most of what learning Rust means. ## What Changed in Rust Recently? Rust ships a new version every six weeks, so the list below stays short and sticks to what a learner will meet. If the schedule holds, the next release is due in mid-November, and the beta channel already carries 1.100. - Rust 1.85 (February 2025): the 2024 edition became stable, along with async closures. - Rust 1.88 (June 2025): let chains, which let one `if` test several patterns, in the 2024 edition only. - Rust 1.95 (April 2026): `if let` guards on match arms, and a `cfg_select!` macro for compile-time choices. - Rust 1.96 (May 2026): new range types that can be copied, and `assert_matches!` for tests. - Rust 1.97 (July 2026): Cargo can control whether warnings fail a build, and the newer symbol mangling scheme became the default. - Rust 1.99 (October 2026): C-style variadic function definitions and a handful of new library methods, with nothing a beginner must learn. The Rust Book now assumes Rust 1.90 or later with the 2024 edition, so its examples match what `cargo new` gives you today. The Rust blog also reported supply-chain attacks and a leaked-secrets problem in 2026, so check what your own dependencies pull in. Check the [Rust 1.99.0 announcement](https://blog.rust-lang.org/2026/10/01/Rust-1.99.0/) for the official summary. ## Learning Path Learn Rust in the order that makes the borrow checker stop feeling like an enemy: 1. Ownership and moves, so you know who is responsible for each value. 2. Borrowing with `&` and `&mut`, and the one-mutable-or-many-shared rule. 3. Structs and enums for modeling data, then `match` for handling every case. 4. `Result` and the `?` operator for errors you can recover from. 5. `Vec`, `String`, and `HashMap`, plus iterator chains with `map` and `filter`. 6. Traits and generic bounds once you want to share behavior across types. 7. Cargo, crates, and tests as the everyday workflow. 8. Async Rust and an executor like Tokio when the work is IO-bound. Once ownership and errors feel routine, go build something small and real. A command-line tool that reads a file, parses it, and prints a summary will teach you ownership, errors, and iterators without a framework in the way. ### When Are You Ready for the Next Step? The eight steps above, with language basics added at the front and a language-choice step at the end, make a nine-step path. Install Rust before you start step one. Read structs and enums early, and save traits and generics for after collections and iterators. | Step | Move on when you can | | 1. Language basics | Write a small program with loops | | 2. Ownership | Explain why a value was moved | | 3. Types | Model data with a struct and enum | | 4. Errors | Return a Result and use ? | | 5. Collections | Store items in a Vec and a HashMap | | 6. Iterators | Swap an index loop for map and filter | | 7. Cargo and testing | Run cargo test and cargo clippy | | 8. Concurrency | Spawn a thread and share data safely | | 9. Choosing a language | Pick Rust or another language | ## What Mistakes Do Beginners Make in Rust? The first row is a compiler error, and the others come from the Rust Book, the Cargo reference and the Rust project's learning research. Most of them are the compiler protecting you, which feels better once you know it is on your side. | Mistake | What happens | Fix | | Using a value after moving it | Error E0382, value moved | Borrow it or clone it | | Avoiding every clone | Lifetime errors eat hours | Clone freely while learning | | Writing inheritance-style objects | Rust has no inheritance | Use structs, enums, traits | | Calling unwrap everywhere | Panics on None or Err | Return a Result instead | | Timing a debug build | Unoptimized, so it looks slow | Time with `--release` | | Starting with async | Hard concepts arrive too early | Learn ownership first | | Following pre-2018 tutorials | `extern crate` lines, old patterns | Check the edition year | | Treating unsafe as checks off | Only five extra operations | Keep unsafe small and reviewed | Two of those cost beginners the most time. Fighting the borrow checker with clever references, when a clone would have been fine, is the slow one. Writing Java-style or C++-style objects is the sneaky one, because the code looks reasonable until the compiler refuses it. ## What Should I Learn First? If you only have an evening, do this in order and skip everything else for now: 1. Install Rust through rustup and run `cargo new` followed by `cargo run`, following the install steps on rust-lang.org. 2. Write a small program with variables, a loop, and one function that returns a value. 3. Make one value move on purpose and read the compiler's message from top to bottom. 4. Clone when the borrow checker fights you, and clean it up later. 5. Return a `Result` from a function and handle it with `?`. 6. Leave traits, lifetimes and async until the earlier steps feel ordinary. Go slowly through ownership now, and everything after it moves faster. ## What to Build First Build a small command-line tool with real input and real failure paths. A log filter or a word-count utility is enough if you treat it seriously: take a file path as an argument, return a `Result` from each fallible step, use `?` to bubble errors, and shape the output with an iterator chain. Hold yourself to this checklist while you write it: - Decide who owns each value, and borrow with `&` instead of cloning by reflex. - Return `Result` from anything that can fail, and handle the error at the edge. - Model the data with a struct or enum before you reach for loose tuples. - Replace index loops with iterator methods where they read more clearly. - Run `cargo fmt`, `cargo clippy`, and `cargo test` before you trust the code. - Get a second version from an AI assistant, then read how it handles ownership and `Result`. When that feels ordinary, the bigger Rust patterns stop looking like ceremony and start looking like the compiler helping you keep promises you already wanted to keep. ## What Can't Rust Do? Rust solves a specific set of problems, and it leaves others alone. Here are the limits that matter most: - Make builds instant: the project's 2025 survey lists slow compile times and storage use among the most reported problems. - Fix a bad design: the compiler checks memory and thread safety, and it has no opinion on whether your program does the right thing. - Catch every bug: safe Rust still allows memory leaks, such as reference cycles, and logic errors that no compiler can see. - Replace a quick script: Python gets a small job done faster, and a first mobile app belongs in Swift or Kotlin. - Skip the learning curve: ownership takes practice, and the project's own interviews say it clicks after a lot of code. Rust can work as a first language if you are patient and your goal needs what Rust offers. If you want quick wins and fast feedback, start elsewhere, and come back once you have a reason to. ## Related Languages Rust is the right starting point when memory safety and performance both matter and you are willing to make ownership explicit. The ownership model is the part that feels new, and it is worth the adjustment. Reach for [Python](/programming/python/) when scripts and data work matter more than control over memory. [Go](/programming/go/) is the lighter option for operational services that value quick iteration. [Swift](/programming/swift/) shares Rust's value-type instincts in a more app-focused setting, and [C#](/programming/csharp/) fits teams already on .NET. Many systems teams keep Rust for the performance-critical core and a higher-level language for the glue. ## Related - [Go](/programming/go/) - a lighter language for operational backend services. - [Swift](/programming/swift/) - value-type-first design for Apple platforms. - [Programming](/programming/) - the broader language and data-structure index. - [Backend development](/programming/topics/backend/) - how servers, APIs and databases fit together in any language. - [APIs](/programming/topics/apis/) - the request, response and status-code ideas a Rust service speaks. - [DevOps](/programming/topics/devops/) - the wider picture of building, shipping and running programs. ## Frequently Asked Questions ### What does fearless concurrency mean? That the ownership and borrowing rules catch data races at compile time rather than in production. Multithreaded code which compiles has already been checked for the shared-mutable-state bugs that cause corruption in languages without that guarantee. ### Can Rust code still leak memory? Yes, it can, because safe Rust prevents use-after-free, buffer overflows, and dangling references, but a reference cycle built with Rc or Arc leaks perfectly safely, because nothing frees data that still has a live reference pointing at it. ### What is unsafe Rust? A keyword that unlocks five extra operations the compiler cannot verify, such as dereferencing raw pointers and calling C functions. It does not turn off the borrow checker or Rust's other safety checks, so you take on responsibility only for those five operations. Idiomatic Rust keeps unsafe code in a small, carefully reviewed boundary rather than spreading it. ### Does Rust have null? Rust has no null pointer, so a value that may be absent is wrapped in Option, and the compiler will not let code use it without handling the None case, which removes null dereferencing as a category of runtime failure. ### Can Rust call C libraries? Yes, through its foreign function interface, where an extern C block declares the signatures, and since the 2024 edition that block must itself be written as unsafe extern. Rust can also expose its own functions for C to call in return, which is how it integrates into existing C and C++ codebases. ### Is Rust harder than Go? Usually yes at the start, because ownership and borrowing add ideas that Go does not have. The Rust project's learning research heard the same story repeatedly: it clicks after a lot of code, and the compiler's messages do much of the teaching. Go usually reaches a first working program faster. ### Should I learn Rust or Go first? Learn Go first if you want working programs quickly, because it has fewer concepts to absorb. Learn Rust first only when memory control or having no garbage collector is the point of your project. Many people learn Go first and add Rust later when a project calls for it. ### Is Rust still growing? Yes, going by the Stack Overflow Developer Survey, where Rust use among respondents rose from 12.6% in 2024 to 14.8% in 2025. The same survey called it the most admired language at 72%, and the Rust project's own survey described organizations hiring more Rust developers. ### Is Rust good for web development? For the server side, yes, because the Rust home page lists network services as a core area, and Axum, from the Tokio project, was the second most admired web framework in the 2025 Stack Overflow survey. In the browser, Rust compiles to WebAssembly, though front ends remain mostly JavaScript and TypeScript territory. ### Which Rust version should I install? Install the current stable release through rustup. Rust 1.99.0 came out on 1 October 2026, and new projects from cargo new use the 2024 edition by default. A new stable version arrives every six weeks, and rustup update installs it. ### What is a Rust edition? An edition is an opt-in bundle of language changes that may break old code, so existing crates keep working until you migrate. Rust 2024 is the latest edition, stable since Rust 1.85 in February 2025, and crates on different editions work together. ## Where to Start Install Rust with rustup, run `cargo new`, and make one value move on purpose so you can read the compiler's message. Then work down the learning path above, starting with ownership. Build the small command-line tool afterwards, because Rust sticks once you have fought it on your own code. --- ## SQL Guide: Queries, Joins, Schema Design, and Databases URL: /programming/sql/ Cluster: Programming > Overview & Programming SQL is the [language](/programming/) that almost every relational database understands, and it is the one skill in this part of the site that outlasts every framework built above it. ## What Is SQL, in Plain Words? SQL, short for Structured Query Language, is the language used to define, read, change, and protect data in a relational database. A database is a set of connected ledgers. Each table holds one kind of fact, and keys link related facts without copying the same details into every row. You describe the result you want, and the database decides how to fetch it. One short query shows the shape of a read against stored orders. ```sql SELECT id, customer_name FROM orders WHERE status = 'paid' ORDER BY created_at DESC LIMIT 10; ``` Read it as a sentence: choose these two columns, from the orders table, where the status is paid, newest first, up to ten rows. The linked guides develop each clause with complete examples. ## What Is a Relational Database? Four nouns carry almost every sentence on the pages below, and the guides assume you already have them. A table holds one kind of fact and nothing else: customers in one, orders in another. A row is a single instance of that fact, and a column is one attribute every row of the table has. The discipline that makes the model work is keeping unrelated facts out of the same table, which is what [schema design](/programming/sql/schema-design/) is about. The fourth noun is what makes the model relational. A key is a column, or a set of columns taken together, whose value identifies a row, and a foreign key holds the key of a row in another table. Instead of copying a customer's name and address into every order, an order stores the customer's id, and the database refuses to store an id that does not exist. That refusal is the point, because the database enforces the relationship instead of relying on whoever wrote the last import script. Everything else in SQL follows from those four ideas. Joins exist to follow keys back to the facts they point at. Indexes exist because finding rows by value gets expensive as tables grow. Transactions exist because one real-world action often changes several tables, and half of it is worse than none of it. ## What Do People Use SQL For? SQL has three main jobs, and most people meet it in the first one. Applications use SQL when several programs share the same customers, orders, or permissions: the web application reads and writes, a background worker updates, and an administration tool corrects. People also use SQL to ask questions of stored data, such as counts, totals, and comparisons for a report. The third job is moving and cleaning data, which covers imports, migrations, and one-off repairs. | The job | Who does it | Guides that own it | | Application storage | Backend and web developers | [Schema design](/programming/sql/schema-design/) and [transactions](/programming/sql/transactions/acid/) | | Reporting and analysis | Analysts, founders, and developers answering a question | [Joins](/programming/sql/joins/) and [window functions](/programming/sql/queries/window-functions/) | | Moving and cleaning data | Anyone holding a CSV file or an old database | [CSV to SQL](/programming/sql/imports/csv-to-sql/) and [UNION](/programming/sql/queries/union/) | | Local and embedded data | Desktop apps, scripts, and test suites | [SQLite](/programming/sql/databases/sqlite/) | The reason this one skill carries across so many jobs is that the data model outlives everything built on it. A website gets rebuilt, a framework gets replaced, and an agent writes a new service, while the tables and the rules inside them stay in service. That is why the guides below spend so much time on schema design before they reach any clever query. ## Do I Still Need SQL When ORMs and AI Write It for Me? Yes, because someone still has to read what was written. An ORM, short for object-relational mapper, is a library that turns objects in your code into SQL on your behalf. The documentation for two popular ORMs says so in its own words. The Entity Framework Core documentation states that SQL queries are useful when the query you want cannot be expressed in its own query language, or when a generated query turns out to be inefficient. Django documents three separate ways to fall back to raw SQL when its model query tools do not go far enough. Coding agents sharpen the same point for anyone using them. In the 2025 Stack Overflow survey, 84 percent of respondents used or planned to use AI tools, and 66 percent named solutions that are almost right, but not quite, as their biggest frustration. Almost right is the dangerous state for a query. A wrong join returns plausible numbers instead of an error, and my own rule on the [window functions guide](/programming/sql/queries/window-functions/) applies: a query that runs and returns a wrong number deserves more suspicion than one that fails. | The layer above | What it writes for you | What you still need to judge it | | An ORM | Queries from objects and relations | Joins and indexes, to see why a page is slow | | A coding agent | Queries and migrations from a plain request | Constraints, transactions, and whether the numbers are right | | A dashboard tool | Reports from a visual builder | Grouping and filtering, to trust the totals | The classic ORM symptom is a page that issues one query for a list and then one more for every row in it. The page works in testing with ten rows and crawls with ten thousand, and the fix is usually a join or an eager-loading option. You only recognize the symptom by reading the SQL the library produced, which is a job for someone who can read SQL. My sense is that the ORM question is mostly a question about who will read that output when something goes wrong. Access is the other half of the question. Some agent tooling now ships with a database tool, and Laravel's documentation for Laravel Boost lists a database query tool and a schema reader among the abilities it gives an agent. Whatever the product, the safe default is a database account that can only read. The PostgreSQL documentation explains that privileges are granted with `GRANT` and that, for most objects, only the owner or a superuser can do anything at first, so a read-only role takes a few lines to set up. Learning SQL is what lets you write those lines and review the agent's. ## SQL or NoSQL? NoSQL is a loose label for databases that do not use the relational table model, and the vague claim attached to it, "use NoSQL for scale", is too vague to design against. I would start relational whenever entities and relationships must stay valid, and reach for another model only when I can name the access pattern it serves. The MongoDB documentation gives that test for its own model: embed related data when entities have a contains relationship or a one-to-many relationship that is read in the context of the parent, and accept that the data gets duplicated. | Model | It stores | It fits when | | Relational (SQL) | Tables, rows, and keys | Relationships must stay valid and reports must add up | | Document | Self-contained JSON-like documents | Data is read together as one unit, and duplication is acceptable | | Key-value | A value under a key, such as Redis strings, hashes, and lists | Fast lookups of caches, sessions, counters, and queues | | Graph | Nodes and the links between them | The links between items are the thing you query | The choice is also less binary than it was. PostgreSQL documents a `jsonb` type that stores JSON in a binary form and supports indexing, and its documentation recommends `jsonb` for most applications. A relational database with a JSON column covers many cases that once pushed teams toward a document store. The [SQL vs NoSQL comparison](/programming/sql/comparisons/sql-vs-nosql/) works through the trade-offs, the consistency questions, and using more than one data store. Many real systems use more than one store, and the sensible pattern keeps one source of truth. The relational database holds the records that must stay correct, while a key-value store such as Redis holds a cache or a session that can be rebuilt. If losing the data would hurt, it belongs in the database with constraints. ## Which Database Should a Beginner Start With: SQLite, PostgreSQL, or MySQL? Learn SQL on SQLite, and start a new hosted application on PostgreSQL unless your host, framework, or existing application already runs MySQL or MariaDB. SQLite is a single file read through a library, with no separate server to install or secure, so the first hour goes into queries. The other three are database servers that applications connect to over a network, which is the shape of most hosted web work. | Engine | How it runs | Start with it when | | [SQLite](/programming/sql/databases/sqlite/) | A file beside the application | You are learning, or one app or device owns the data | | [PostgreSQL](/programming/sql/databases/postgresql/) | A database server | Constraints, JSON, or extensions carry real requirements | | [MySQL](/programming/sql/databases/mysql/) | A database server | Your host, framework, or existing app already targets it | | [MariaDB](/programming/sql/databases/mariadb/) | A database server | The stack ships it, as WordPress hosting often does | The SQLite project is candid about its limits. It says that any site getting fewer than 100,000 hits a day should work fine on SQLite, and that a client-server database is better when many clients must write at once, because SQLite allows one writer at a time. The deciding factor is the shape of the deployment, and the [MySQL vs PostgreSQL vs SQLite comparison](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/) walks through it. What I like about all four is that they are open source, so I can read the license, run them on my own server, and keep the data there. The cost is that backups, upgrades, and tuning become mine, and a backup that has never been restored is only a hope. MySQL and MariaDB are close relatives and not interchangeable, which the [MySQL vs MariaDB vs MaxDB comparison](/programming/sql/comparisons/mysql-vs-mariadb-vs-maxdb/) explains. ## Will I Have to Relearn SQL When I Switch Databases? Very little changes, and that is the main argument for learning it at all. Selections, filters, joins, inserts, updates, and deletes read almost the same on every engine, so the shop database you build in SQLite will move to PostgreSQL or MySQL with small edits. The differences gather at the edges: how generated keys are declared, how an upsert is written, which JSON and date functions exist, how text is compared and sorted, and which administrative commands manage users and backups. The practical rule is to test on the exact engine and version you will deploy. A query that runs on MySQL can fail on PostgreSQL for reasons that have nothing to do with correctness, and a migration that passed on a laptop SQLite file can behave differently under a server's locking. Moving your practice database to a second engine is a cheap way to learn which of your habits were SQL and which were dialect. The [comparisons hub](/programming/sql/comparisons/) lists where the engines diverge. ## Who Uses Which Database, and Why? The Stack Overflow Developer Survey gives the broad picture. PostgreSQL has been the most used database in the survey since 2023, and it moved from 45.55 percent of all respondents in 2023 to 55.6 percent in 2025. MySQL held close to 40 percent across the same three years, while SQLite climbed from 30.9 percent to 37.5 percent. | Engine | Survey usage, 2023 / 2024 / 2025 | What its users say about it | | PostgreSQL | 45.6 / 48.7 / 55.6 percent | Notion described growing from one Postgres database to 96 sharded instances in a 2023 post | | MySQL | 41.1 / 40.3 / 40.5 percent | GitHub's 2023 post counts more than 1,200 MySQL hosts in over 50 clusters | | SQLite | 30.9 / 33.1 / 37.5 percent | The SQLite project lists every phone, browser, and Windows 10 or 11 installation among its homes | | MariaDB | 17.2 percent in 2024, 22.5 percent in 2025 | WordPress recommends MariaDB 10.11 or later, or MySQL 8.0 | Company engineering posts add the reasons behind these numbers. Shopify's engineering blog describes its platform as many pods, each an isolated instance with its own MySQL shard beside Redis and Memcached. Uber's 2016 post describes the opposite move, from Postgres to MySQL, and gives reasons tied to its own workload: write amplification, replication that consumed too much bandwidth between data centers, and upgrades that needed extensive downtime. Those reasons describe Uber at that date and that scale. A beginner's project sits a long way below it, so treat the post as a reminder that engines trade off, and not as a verdict on either one. A named company choosing an engine is evidence about its workload and not about yours. The reliable pattern across all of these posts is that teams pick for operations, meaning backups, failover, upgrades, and the skills they already have, and then spend years on the data model. The data model outlives every engine choice, which is why the [schema design guide](/programming/sql/schema-design/) matters more than the logo on the server. ## Where Did SQL Come From, and Is It Going Away? IBM's history of the relational database dates the idea to 1970, when Edgar F. Codd published "A Relational Model of Data for Large Shared Data Banks". IBM's System R project began in 1973 as an industrial-strength implementation, where Donald Chamberlin and Raymond Boyce developed SQL, and DB2 shipped in 1983. The language is therefore older than the web, and it has outlived every programming fashion since. The engines have their own histories, which matter for different reasons. The PostgreSQL project traces its code to the POSTGRES package begun at Berkeley in 1986, and it says SQL support arrived in 1994 and the PostgreSQL name in 1996. The MySQL manual says the project began as an alternative to mSQL, and that it is named after Monty Widenius's daughter, My. The [interview with Monty Widenius](/interviews/02-monty-widenius-mysql/) tells the MySQL story in his own words. On whether SQL is going away, the survey says no. SQL was used by 48.66 percent of all respondents in 2023, 51 percent in 2024, and 58.6 percent in 2025. Among professional developers it reached 61.3 percent in 2025, which put it third behind JavaScript and HTML and CSS, ahead of Python. The history also sets one expectation: the language is standardized, but each engine has its own dialect. PostgreSQL states that it conforms to at least 170 of the 177 mandatory features for SQL:2023 Core as of version 18, and the word at least tells you the rest is engine-specific. ## In What Order Does SQL Run a Query? Here is the rule the rest of SQL assumes you already know, and it is not the order you write the clauses in. A query is written starting with the columns to show, but the language defines its meaning starting with the tables. The logical sequence is to assemble rows from the tables and their joins, discard rows with the filter, collapse what is left into groups, discard groups with the group filter, work out the output columns, sort, and cut to a row limit last of all. That order describes what a query means, and an optimizer is free to do the physical work in any order that produces the same answer faster. Three common surprises follow directly from that sequence. A column alias created in the output step usually cannot be used in the row filter, because the filter runs first. The row filter and the group filter are not interchangeable, since one filters rows before grouping and the other filters groups after. A row limit of ten does not mean the database only did ten rows of work, because the limit runs after everything else. Once this sequence is in your head, most questions about why SQL did something answer themselves, and query plans stop looking arbitrary. The [indexes and query optimization guide](/programming/sql/indexes/query-optimization/) shows how to read a real plan with `EXPLAIN`, and the [joins guide](/programming/sql/joins/) shows the first step of the sequence at work. ## How Hard Is SQL to Learn, and Do Agents Change That? The first week is easy, since a query reads close to English and one small table is enough to practice on. The difficulty arrives later and has little to do with syntax: thinking in sets of rows instead of loops, designing tables so that impossible states cannot be stored, and reading a plan to see why something that was fast with test data is slow with real data. An agent can type any query for you, and typing was never the hard part. The order below follows the questions a small shop database forces on you, because each one needs the next idea. 1. [Schema design](/programming/sql/schema-design/): create users, products, orders, and order items with real foreign keys rather than loose integer columns. 2. [Joins](/programming/sql/joins/): answer which orders belong to this customer, which needs more than one table. 3. [Indexes and EXPLAIN](/programming/sql/indexes/query-optimization/): find out why that query got slow at ten thousand rows. 4. [Transactions](/programming/sql/transactions/acid/): take payment and reduce stock so that neither ever happens without the other. After those four, the next steps depend on what you build. Each guide below answers one follow-up question, and none of them needs the others. - [Window functions](/programming/sql/queries/window-functions/): ranking and comparing rows inside a group, the usual next step for reports. - [UNION and UNION ALL](/programming/sql/queries/union/): stacking result sets that share the same columns. - [CSV to SQL](/programming/sql/imports/csv-to-sql/): bringing outside data in through a staging table. - [SQL interview questions](/programming/sql/interview-questions/): rehearsing the reasoning an interviewer probes. - [A second engine](/programming/sql/databases/): moving the same shop database to PostgreSQL or MySQL to see which parts were dialect. Agents change the cost of the first draft and leave the cost of review unchanged, which is the same bargain they offer in every language. The table lists what to hand over and what to keep. | Hand it to the agent | Keep for yourself | | A first draft of a query from a plain-English request | Checking the result against a count you know is right | | A draft migration that adds a column or table | Reading it for locks, rollback, and data loss before it runs | | An explanation of a slow query | Running EXPLAIN on real data and trusting the plan | | A script to import a file | The staging table and the validation before the real write | I learned query plans in the early 2000s on shared hosting, with nothing but a plain `EXPLAIN` for guidance, and that habit of asking the database what it actually did has aged better than any tool I used then. Treat an agent as a contractor: right project, narrow task, a read-only account, and a diff you read before it runs. The [terminal AI agents guide](/programming/topics/devops/terminal-ai-agents/) covers those permission habits. ## What Mistakes Do Beginners Make with SQL? - Concatenating input into SQL: use prepared statements and bound values, as the [PDO guide](/programming/php/mysql/pdo/) shows. - Assuming row order: add an explicit `ORDER BY` with a stable tie-breaker. - Skipping constraints: enforce stored rules in the database as well as the application, as the [schema design guide](/programming/sql/schema-design/) explains. - Adding speculative indexes: inspect a real query plan and include the write cost, as the [indexes guide](/programming/sql/indexes/query-optimization/) shows. - Assuming full portability: run migrations and queries on the exact database engine and version. ## Should Rules Live in the Database or in My Code? Put every rule that all writers must obey in the database, and keep the friendly wording in the application. The writers are more than your web application: a migration, an import script, a background worker, a second service, or a person in a database client can all write to the same tables. A rule kept only in application code is a rule that only that application keeps. | The rule | Where it lives | Why | | An order must point at a customer who exists | A foreign key in the database | Every writer is held to it, including the next import script | | Two accounts cannot share an email address | A unique constraint, plus a friendly message in the application | Two requests arriving together can both pass an application check | | Taking payment and reducing stock happen together | A transaction around both writes | Half of a real-world action is worse than none of it | | The wording of an error message | The application | Only the application knows who is reading it | This is the position I defend most often, because the alternative fails slowly and out of sight. An application check is fine for the message, and the constraint is what stays true when somebody forgets the check. I think of the constraint as the cheaper insurance, since it costs one line of schema and covers every writer. The [schema design guide](/programming/sql/schema-design/) covers keys and constraints, and the [transactions guide](/programming/sql/transactions/acid/) covers the rule about writes that belong together. ## Can I Run the Database Myself? You can, and I would encourage it, as long as you secure it and keep backups you have restored at least once. A database you run yourself keeps the data on hardware you choose and keeps the configuration in readable files. The price is that patching, upgrades, backups, and failure at three in the morning are yours. | Option | You look after | The question to ask first | | A SQLite file | Copying the file safely, and backups | Does only one application write to it? | | A server you run | Users, patching, upgrades, backups, and restores | Who is on call when it fails? | | A managed database service | Your schema and queries; the provider runs the server | Can I leave with a full dump if the terms change? | Whichever you choose, give each application its own restricted database user and keep the master credentials away from ordinary requests. Each engine documents its own backup route, including the SQL dump for PostgreSQL, the backup and recovery chapter for MySQL, mariadb-dump for MariaDB, and the backup API for SQLite. The engine guides linked below turn those routes into steps, and the [DevOps hub](/programming/topics/devops/) covers the server skills around them. ## What Can't SQL Do? SQL describes what you want and leaves out how to get it. The query planner chooses the access path from statistics about the data, so the same query can get slower as a table grows without a line of it changing. Several engines offer hints to force an index or a join order, but they pin a decision to today's data and quietly rot as the data changes. [Reading the query plan](/programming/sql/indexes/query-optimization/) beats both guessing and overriding. It also cannot protect you from a schema that permits bad data. A constraint the database does not enforce will eventually be violated by something: a migration, a background job, a second application, an afternoon in a database client. [Schema design](/programming/sql/schema-design/) exists because rules kept only in application code are rules that only that application keeps. The third boundary is the one that causes incidents. SQL cannot tell your values apart from your query text once they have been concatenated into one string. The remedy is a prepared statement that sends the query and the values separately, and it is covered in both the [PDO guide](/programming/php/mysql/pdo/) and [application security](/appsec/). ## Which Guides Teach the Core of SQL? These guides carry the ideas that every other SQL page assumes, listed in the order a small project usually needs them. - [SQL schema design](/programming/sql/schema-design/) answers how to give each fact one home, with keys, constraints, and safe migrations. - [SQL joins](/programming/sql/joins/) answers how to read across tables with inner and left joins, and how rows match or fail to match. - [SQL indexes and query optimization](/programming/sql/indexes/query-optimization/) answers why a query is slow and how to read a real plan before adding an index. - [SQL transactions and ACID](/programming/sql/transactions/acid/) answers how several writes commit together, and what rollback, locks, and retries do. - [SQL UNION and UNION ALL](/programming/sql/queries/union/) answers how to stack compatible result sets, and when a union hides a schema problem. - [SQL window functions](/programming/sql/queries/window-functions/) answers how to rank and compare rows within a group without collapsing them. - [SQL common table expressions](/programming/sql/queries/common-table-expressions/) answers how `WITH` and `WITH RECURSIVE` name a subquery and walk a hierarchy. - [Common SQL interview questions](/programming/sql/interview-questions/) answers what an interviewer probes on joins, grouping, indexes, constraints, and transactions. ## Which Guide Shows Me How to Do One Specific Thing? - [Import a CSV file into SQL](/programming/sql/imports/csv-to-sql/): through a staging table and a validation step before the real write. - [Decide whether images belong in a database](/programming/sql/storage/storing-images/): or in object storage, weighed by size, atomicity, and recovery. - [Build database search with PHP and SQL](/programming/php/mysql/search-application/): without concatenating input into queries. - [Combine result sets](/programming/sql/queries/union/): with UNION and UNION ALL, without confusing unions with joins. - [Group related writes in a transaction](/programming/sql/transactions/acid/): and handle rollback, locks, and retries. ## Which Guides Help Me Choose and Run a Database? Start with the [SQL databases hub](/programming/sql/databases/) for where PostgreSQL, MySQL, SQLite, and MariaDB fit, and with the [SQL comparisons hub](/programming/sql/comparisons/), which separates workload decisions from popularity rankings. - [PostgreSQL](/programming/sql/databases/postgresql/): roles, JSONB, indexes, backups, and upgrades. - [MySQL](/programming/sql/databases/mysql/): restricted users, InnoDB, backups, and major upgrades. - [SQLite](/programming/sql/databases/sqlite/): command-line inspection, WAL mode, backup, and migration. - [MariaDB](/programming/sql/databases/mariadb/): MySQL compatibility, storage engines, and configuration files. - [MySQL vs PostgreSQL vs SQLite](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/): choosing by deployment shape, write concurrency, and recovery. - [MySQL vs MariaDB vs MaxDB](/programming/sql/comparisons/mysql-vs-mariadb-vs-maxdb/): why these three are separate routes and not substitutes. - [SQL vs NoSQL](/programming/sql/comparisons/sql-vs-nosql/): relational against document, key-value, and graph models. ## Which Guides Connect SQL to an Application? - [PHP and MySQL with PDO](/programming/php/mysql/pdo/) answers how to bind values safely and handle database errors from PHP. - [PHP and MySQL applications](/programming/php/mysql/dynamic-sites/) connects the database ideas to a complete web request. - [C# frameworks](/programming/csharp/frameworks/) covers Entity Framework Core, the ORM that sits between a .NET application and its database. - [Backend development](/programming/topics/backend/) places the database beside the other server-side choices. ## Frequently Asked Questions ### What should a beginner learn first in SQL? Start with `SELECT`, `FROM`, `WHERE`, `ORDER BY`, and `LIMIT` on one small table. Then learn joins, grouping, keys, constraints, indexes, and transactions in that order. Each step adds one new way to read, connect, protect, or speed up stored facts. ### Do all SQL databases use the same syntax? They share common syntax for selections, filters, joins, inserts, updates, and deletes, but their dialects differ at the edges. Generated keys, upserts, JSON functions, date arithmetic, collations, and administrative commands must be tested on the exact engine and version. ### Which SQL database should a beginner use? SQLite is the shortest route to local practice because it needs no separate server. PostgreSQL, MySQL, or MariaDB is a better starting point when the goal is a hosted client-server application and the chosen framework or provider supports that engine. ### Can application validation replace SQL constraints? It cannot, because another script, worker, migration, or administration tool can write to the same database. Application validation can provide better messages, but use SQL constraints for rules every writer must obey, then use transactions when several related changes must succeed together. ### What is the difference between a primary key and a foreign key? A primary key identifies a row uniquely within its own table. A foreign key is a column that points at another table's primary key, and the database enforces that the target actually exists. One names a row, the other links to one. ### Do you still need SQL if you use an ORM? You do, because an ORM writes SQL for you and you will be the one reading it when a page is slow or a result is wrong. Understanding joins, indexes, and transactions is what lets you tell whether the generated query is reasonable. ### What is NoSQL and when would you use it instead? NoSQL covers databases that do not use the relational table model, such as document and key-value stores. They suit data that is read together as one unit or very high-volume access patterns. Choose relational when the data has clear relationships and you need constraints to protect them. ## Where to Start Create one small shop database in SQLite and query it: users, products, orders, and order items, with real foreign keys. Answer the four questions in the learning order above against it, because each one forces the next concept, and open [schema design](/programming/sql/schema-design/) the first time the data itself fights you. Four questions on one small database teach more than four guides read in a row. --- ## Swift Guide: Apple Platforms, Types, Sets, and Collections URL: /programming/swift/ Cluster: Programming > Overview & Programming Swift is Apple's language for building apps across iOS, macOS, watchOS, tvOS, and visionOS. It is also a general-purpose language with static types, optionals, protocols, and structured concurrency, and you can use it well beyond Apple's own platforms. For help choosing a coding assistant, see our [AI coding tools](/ai-coding-tools/) ranking. ## What Is Swift, in Plain Words? Swift is a compiled, statically typed language from Apple. Compiled means your code is turned into machine code before the app runs, and statically typed means the compiler checks what kind of value each name holds before you ship. That is why so many mistakes show up as a build error on your screen instead of a crash in front of a customer. Swift has one job it takes seriously, which is making the shape of your program visible. Missing values show up through optionals, a type that says a value might be absent. Work that may pause shows up through `await`, and shared mutable state has to have an owner. That visibility feels fussy when you are new to it, and it saves you from a whole category of bugs once the app gets real. Three other words come up constantly in Swift conversations. Xcode is Apple's free app-building tool for the Mac. SwiftUI is a framework written in Swift for describing screens, and UIKit is the older framework that many existing apps still use. Swift is the language, and those two are libraries you use with it. ## Should I Learn Swift, React Native, or Flutter for My First App? Start from where your users are, then pick the tool. If your first app is for iPhone, or you want the most native Apple feel, Swift with SwiftUI is the direct route. If you need iPhone and Android from one codebase on day one, React Native and Flutter exist for exactly that, and the arguments about them get as heated as Wawa versus QuickChek. | Option | Language | Platforms | Best when | | Swift and SwiftUI | Swift | Apple platforms | Your first app targets iPhone | | React Native | JavaScript | iOS and Android | You already know React | | Flutter | Dart | Mobile, web, desktop | You want one toolkit everywhere | React Native's own site says React primitives render to native platform UI, so your app uses the same platform APIs other apps do. Flutter describes itself as an open-source framework for building natively compiled, multi-platform apps from a single codebase, and it is powered by Dart. Both give you one codebase for two phone platforms, and the price is learning a separate framework instead of the tools Apple builds Xcode around. | If this matters most | Lean toward | Why | | Android on day one | React Native or Flutter | One codebase reaches both phones | | Apple platforms only | Swift | Xcode offers SwiftUI for new apps | | You know web development | React Native | It uses JavaScript and React | | A language that travels | Swift | Official Linux and Windows toolchains | My pick for a first iPhone app is Swift, because the language, Xcode, and Apple's learning material all point the same way. That is a pick about Apple's platform, and it changes the moment Android parity matters. Python is the wrong tool here, since it stays niche on phones, and a first mobile app belongs in Swift or Kotlin. ## When Should I Use Swift, and When Should I Skip It? Swift is the natural choice when you are building for Apple platforms: SwiftUI and UIKit apps, widgets, App Intents, extensions, command-line tools for macOS, and code that talks to Foundation and the system frameworks. Use it when the platform matters and the data model matters. A settings screen, a health app, a camera workflow, or a watch complication all benefit from a language that forces you to name missing data, ownership, and UI state. If you are mostly writing a small web API and nobody on the team knows Swift, start with the stack the team can maintain. Swift can also run on the backend, but an unfamiliar language adds learning and maintenance work. ## Is Swift Only for Apple Platforms? No. Swift is open source, and swift.org offers installs for macOS, Linux, and Windows. What stays tied to Apple are the UI frameworks, which is why a Windows machine can compile Swift but cannot build an iPhone screen. | Where | Status today | Catch | | Apple platforms | Home turf, all of them | Xcode needs a Mac | | Linux and Windows | Official toolchains | No Apple UI frameworks | | Servers | Used in production by Apple and others | Check hosting and libraries | | Android | Official SDK, early previews | Young ecosystem | | WebAssembly and embedded | SDK and embedded support | Specialist territory | Android deserves a closer look than the table can give it. The Swift project's Android workgroup published nightly previews of an official Swift SDK for Android in October 2025, noting that over 25% of the packages in the Swift Package Index already built for Android. Swift 6.4, released in September 2026, added Android availability attributes and native Android support in Swift Package Manager. The announcements talk about the SDK and Java interop and say nothing about a Swift UI toolkit for Android, so my read is that today it helps you share code more than it replaces React Native or Flutter. Apple itself describes Swift as adaptable for embedded devices and server infrastructure, and its June 2026 announcement says Swift now reaches from mobile apps and embedded firmware to platforms beyond the Apple ecosystem. ## Who Uses Swift, and Is It Growing? Apple is the biggest user, and it publishes its reasons on swift.org. A June 2026 post describes Apple rewriting its TrueType hinting interpreter from C to Swift, because font parsers handle untrusted data and memory safety matters there. Apple reports that the Swift version runs 13% faster than the C interpreter it replaced. A June 2025 post describes Apple moving its Password Monitoring service from Java to Swift, with close to 40% more throughput and about 85% fewer lines of code. Outside Apple, Cultured Code wrote on swift.org in February 2025 about moving the cloud backend for its Things app from Python 2 on Google App Engine to Swift. It reports a more than threefold reduction in compute costs. These are company accounts of their own projects, so read them as evidence of what is possible and test the claim against your own workload. On popularity, the 2025 Stack Overflow Developer Survey puts Swift at 5.4% of all respondents and 5.7% of professional developers, up from 4.7% and 4.9% in 2024. Kotlin, the other native mobile language, sits at 10.8%, and Dart, the language behind Flutter, at 5.9%. Those figures describe who answered the survey, so treat them as a rough guide to a smaller language with a steady base rather than as market share. ## Where Is Swift Heading, and Where Did It Come From? Apple introduced Swift in June 2014 as a new language for iOS and OS X apps that could sit alongside Objective-C. It opened the source in December 2015 under the Apache 2.0 license, with Linux support from the start. Since then the language has grown through open workgroups, and Swift 6.4 shipped on September 15, 2026, with the project announcing a Networking Workgroup that June. That history changes what you click next. Older tutorials teach Objective-C habits or apps built only on UIKit, and a newer arrival such as Swift 6 added compile-time data-race checking, which is why generated code that touches the UI from the wrong place gets flagged. For a new project, start in Swift and SwiftUI, and read Objective-C only if you inherit an old codebase. ## Is Swift Easy to Learn, and Do Xcode's AI Agents Change That? Apple describes Swift's syntax as easy to read and aimed especially at beginners, and it offers free learning material, including the Develop in Swift tutorials, Swift Playground, and The Swift Programming Language book. The language is friendlier than its reputation. The longer climb is the platform: Apple's frameworks, the app lifecycle, and App Store review, which is where most of your time goes once the basics land. Agents are now built into the tool you will use. Apple announced Claude Agent and Codex support in Xcode 26.3 in February 2026. Xcode 27, announced in June 2026, brings agents from Anthropic, Google, and OpenAI that can write and run tests, try ideas in Playgrounds, and check visual changes with previews. That changes the cost of learning in two ways. Typing Swift gets cheap, so a beginner can see a working screen on day one. Not understanding Swift gets expensive, because AI assistants write fluent Swift, and that fluency is the trap. The code often compiles on the first try while hiding a force-unwrapped optional or a UI update running off the main actor. Here is the one small example worth recognizing on sight. ```swift let bio: String? = nil print(bio ?? "No bio yet") ``` This small program prints No bio yet to the console. The question mark makes `bio` an optional, so it may hold nothing, and the `??` supplies a fallback. A shortened generated version might write `bio!` instead, which crashes the app when the value is missing. Spotting that difference is the whole review skill in miniature. | What the agent does | What you still own | | Drafts a SwiftUI screen | Reading how optionals are unwrapped | | Runs tests and previews | Choosing what is worth testing | | Fixes compiler errors | Understanding why the compiler objected | | Adds async code | Checking what runs on the main actor | Which agent or model to pick inside Xcode changes month to month, so we keep that order in one place. The [AI coding tools](/ai-coding-tools/) page holds the current ranking, and we do not repeat it here. Our call on the Swift side is to let an assistant produce a first draft of a screen, then read the optional handling the way you would read your own code. ## How Do I Start Learning Swift? Shipping an Apple-platform app with Xcode requires a Mac. Download Xcode free from the Mac App Store, open a Swift Playground, and type code where you can watch the result appear beside it. That immediate feedback makes optionals and closures easier to explore than starting inside a full app. Swift Playground on iPad is another way to learn, but it does not replace every Xcode build, signing, and deployment workflow. For a quick standalone check outside any project, save a file like `main.swift` and run it with `swift main.swift` once the Swift toolchain is installed. When the core language stops feeling foreign, create your first real project through File, New, Project, and pick the App template with SwiftUI as the interface. That single screen is where most beginners meet SwiftUI for the first time. ## What Should I Learn First? Learn Swift the language before any framework, in the order that makes generated app code stop looking like magic. 1. Constants and variables with `let` and `var`, plus type inference. 2. Optionals until `?`, `if let`, `guard let`, and `??` feel ordinary. 3. Functions and closures, then a look at what each closure captures. 4. Arrays, dictionaries, and sets, chosen by lookup, order, and uniqueness. 5. Structs and enums for modeling data before you reach for classes. 6. Protocols, extensions, and generics once the basics land. 7. Async/await and main-actor boundaries, then one small SwiftUI feature with loading, empty, and error states. ## What Should I Build First? Build a small counter screen, then add one real input and one failure state. That keeps the first result runnable while you learn how state drives a SwiftUI view. The next project can be a profile screen that loads from the network, where you model loading and error states as separate cases and unwrap optionals honestly. A notes app, a habit tracker, or a tiny file organizer works just as well. When that feels ordinary, SwiftUI and the platform frameworks stop looking like a pile of magic attributes and start looking like code you can review. ## Which Swift Guide Should I Read Next? Four guides go deeper than this page can, and they read in this order. - [Sets in Swift](/programming/swift/collections/sets/) covers the collection built for uniqueness and membership, including set algebra, `Hashable` conformance for your own types, and when an array is the better choice. - [allSatisfy in Swift](/programming/swift/collections/allsatisfy/) shows the one-line way to check that every element of a collection passes a test, plus the empty-collection result that surprises people. - [Structs vs Classes in Swift](/programming/swift/types/structs-vs-classes/) is the comparison to read before you model anything, because value semantics against reference semantics drives inheritance, ARC, mutability, and identity. - [Understanding typealias in Swift](/programming/swift/types/typealias/) covers naming closure, tuple, dictionary, and generic types, and the case where a wrapper struct fits better. ## Where Does Swift Fall Short? Swift has limits, and a few of them matter for a first project. Server-side Swift is real, and frameworks such as Vapor and Hummingbird can run production APIs. Before choosing it for a backend, check whether your team, hosting platform, libraries, and operational tooling support it as well as the alternatives you already use. Cross-platform mobile is the other gap. React Native and Flutter both target iOS and Android from a single codebase, and teams that need Android parity on day one usually reach for one of those. Swift and SwiftUI own the Apple side. The Android SDK is a real step, yet it is young, so check how far it has come before betting a product on it. Swift is a language with a clear home, so pick it when the platform fits. ## Should I Pick Swift or Another Language? Swift is the right starting point when the Apple platform is central. If you are coming from another language, the value-type-first habits are the part that feels new, and they are worth the adjustment. Many app developers keep Swift for the client and reach for another language only when the project needs a server too. - [Python](/programming/python/) fits scripts, data work, and backend prototypes better than native apps. - [Go](/programming/go/) is the easier sell when deployment simplicity and backend concurrency are the center of gravity. - [Rust](/programming/rust/) fits when memory safety and low-level control matter most. - [C#](/programming/csharp/) fits when a mature .NET platform suits the team. - [Programming](/programming/) is the broader language index for everything else. ## Frequently Asked Questions ### Can I build Android apps with Swift? Not with a mature, Swift-only toolkit yet. The Swift project publishes an official Android SDK and Swift 6.4 added Android build support, but the announcements focus on sharing code and Java interop rather than a Swift UI toolkit for Android. ### Should I learn SwiftUI or UIKit first? Start with SwiftUI for a new project, once you can read core Swift, since Xcode offers it for new app projects. Learn UIKit later if you join an existing codebase, because many established apps and some platform work still depend on it. ### Is Swift open source? Yes, Apple open sourced Swift in December 2015 under the Apache 2.0 license, and the language is now developed in the open through swift.org and its workgroups. ### Can you write Swift on Windows or Linux? Yes, because Swift has official Linux and Windows toolchains, and it is used for server-side work with frameworks such as Vapor. What does not cross over is Apple's UI frameworks, so building an iOS interface still needs Apple's tooling. ### Do you need a Mac to build iOS apps? In practice you do, because Xcode runs only on macOS and submitting to the App Store depends on it. Cloud Mac services and CI runners let you build and sign without owning one, but something running macOS is doing the work. ### What is the difference between Swift and SwiftUI? Swift is the programming language, and it stands on its own without any interface. SwiftUI is a framework written in Swift for building interfaces declaratively. You can write Swift with no interface at all, and older Apple apps use UIKit or AppKit instead of SwiftUI. ### Is Objective-C still worth learning? Only if you work on an existing Apple codebase. Objective-C remains supported and large apps still contain it, but new projects start in Swift. Reading it is a more useful skill than writing it. ### How long does it take to learn Swift? Basic syntax takes a few weeks of regular practice if you already program. Shipping an app takes longer, because most of the work is learning Apple's frameworks, the app lifecycle, and the review process rather than the language itself. ## Where to Start Install Xcode on a Mac, open a Swift Playground, and type the optional example from this page to see it print. Then read [Structs vs Classes in Swift](/programming/swift/types/structs-vs-classes/) and build the counter screen. Once core Swift feels ordinary, add SwiftUI and let an assistant draft screens that you review line by line. --- ## TypeScript: Complete Guide URL: /programming/typescript/ Cluster: Programming > Overview & Programming TypeScript is JavaScript with a checker in front of it. You write down what your data should look like, and your editor flags the code the moment it drifts away from that shape. What ships to the browser is still plain JavaScript. If you are still weighing it against the plain language, the [JavaScript vs TypeScript comparison](/programming/javascript-vs-typescript/) compares their benefits and trade-offs. ## TypeScript at a Glance The live part of this section splits into three topics, roughly in the order most people meet them. | Area | What it covers | Start here if | | [Types](/programming/typescript/types/) | Annotations, interfaces, unions, runtime data | You are writing your first TypeScript | | [Generics](/programming/typescript/generics/) | Function types, type parameters, utility types | You keep repeating the same shapes | | [Tooling](/programming/typescript/tooling/) | tsconfig.json, checking, React, API contracts | The build or the config fights you | ## What Is TypeScript, in Plain Words? TypeScript is a programming language that Microsoft announced on 1 October 2012. The team built it for what they called JavaScript development at application scale, meaning code bases big enough that nobody can hold them in their head anymore, and it adds one big thing to JavaScript, which is a static type checker. A type describes the shape of a value, like "a user has an id and an email, and the email is always text." An annotation is where you write that shape down. Inference is the checker working out a shape on its own when you did not write one. Picture a building inspector who reads the plans before anyone pours concrete. Finding the missing doorway on paper costs you an eraser, while finding it after the walls are up costs you a sledgehammer. TypeScript reads your files before anything runs, and it reports every place where the code disagrees with the shapes you stated. One rule sits under everything else on this page. Types exist while you work and disappear before the code runs, so browsers and Node only ever execute JavaScript. A type therefore cannot vouch for data that arrives while the program is running, like a server response or a form submission (the two places bad data usually walks in), and those still need a real check. The [unknown, any, and runtime data guide](/programming/typescript/types/unknown-any-runtime-data/) shows how. ## Why Would You Use TypeScript, and Does It Pay Off? TypeScript earns its keep where code has real boundaries. A boundary is any place where data crosses from one part of the program to another: an API response, a database row, a React prop, a command-line flag, or a function that three different files call. It costs you a check step, some configuration (a tsconfig.json file, mostly), and a little more to read in every file. The table lists what you get for that price. | Area | You get | You pay | | Mistakes | Typos and wrong shapes caught early | A check step to keep running | | Editor | Autocomplete and safer renames | A config file to learn | | Teamwork | Shapes written down for readers | More text in every file | | AI output | A mechanical first reviewer | False comfort when any creeps in | The catch is that types only help when they are honest. A project full of `any`, oversized object shapes, and forced assertions turns the checker into decoration, and decoration does not catch bugs. ## Should I Learn TypeScript or JavaScript First? Learn plain JavaScript first, and add TypeScript when the project asks for it. TypeScript sits on top of JavaScript behavior, so closures, promises, objects, and arrays all still apply, and learning both at once hides which half is causing your problem. I'd move to TypeScript once arrays, objects, and functions feel natural, or once a project grows past a few files. Most beginners seem to agree with that order. In the 2025 [Stack Overflow Developer Survey](https://survey.stackoverflow.co/2025/technology), 62.8% of people learning to code used JavaScript and 31.9% used TypeScript. | Your situation | Start with | Why | | Brand new to code | JavaScript | Fewer moving parts to untangle | | Comfortable with objects and functions | Add TypeScript | Your shapes finally have names | | Joining a team that uses it | TypeScript, JavaScript beside it | The config already exists | | Know Java, C#, or Swift | Either one | The type ideas look familiar | Our [JavaScript guide](/programming/javascript/) covers the language underneath, and the comparison page takes the full side-by-side. ## Do I Need TypeScript for a Small Project? A small project does not need it. A throwaway script, a prototype, or a one-page site is fine in plain JavaScript, and that is often the right call. The math changes once a project keeps changing and other people have to touch it. | Project | TypeScript? | Reason | | One-file script | Skip it | You can hold it in your head | | Small personal site | Optional | Editor help may be enough | | App with an API | Yes | Data shapes cross many files | | Library other people install | Yes | Types double as documentation | Small does not have to mean untyped, though. Node can now run a TypeScript file directly, which removes most of the setup that used to make TypeScript feel heavy for a tiny project. The Node section below explains what that does and does not give you. ## What Is TypeScript Used for, and Who Uses It? It shows up wherever a JavaScript code base has grown past what one person can remember. Typical homes are front ends with shared component props and server data, full-stack apps where route handlers and screens share models, libraries and SDKs other developers install, and backend services with request and job payloads. Company posts show why teams make the move. Slack wrote in 2017 that porting its desktop app surprised the team with the number of small bugs it found, and that the editor suddenly knew which properties and methods each object had. Stripe moved more than 3.7 million lines of code from Flow to TypeScript in one pull request on 6 March 2022. Its post says Flow's memory use could lock up laptops, and that the in-editor integration was frequently slow and unreliable. Those are two big teams with big code bases, and your project might not look like either. The survey gives the wider picture. TypeScript was used by 43.6% of all respondents in 2025 and 48.8% of professional developers. ## Is TypeScript Still Growing, and Where Did It Start? TypeScript started as a Microsoft project in 2012, and the numbers now say it is growing very fast. GitHub's [Octoverse report for 2025](https://github.blog/news-insights/octoverse/octoverse-a-new-developer-joins-github-every-second-as-ai-leads-typescript-to-1/) says that in August 2025 TypeScript became the most used language on GitHub by contributor count, passing Python by roughly 42,000 contributors. It gained over a million contributors in a year, a 66% jump, while JavaScript grew 24.79% over the same period. GitHub gives two reasons for the jump in its report. Nearly every major frontend framework now scaffolds new projects with TypeScript by default, and typed languages make agent-assisted coding more reliable. Treat the ranking as a contributor count rather than a job-market measure, because contributors are not hours coded or jobs posted. The compiler itself changed this year, and that matters for older advice. The team said TypeScript 6.0, released in March 2026, was meant to be the last release built on the original JavaScript code base. The team's [TypeScript 7.0 announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/), published on 8 July 2026, describes a native port written in Go with speedups between 8x and 12x on full builds. So any tutorial that tells you to live with slow type checking predates that rewrite, and I'd treat it as dated advice. The announcement also says 7.0 ships without a programmatic API and that the team expects 7.1 to bring a new one. It names Vue, MDX, Astro, Svelte, and Angular template checking as tools that embed TypeScript and will likely stay on 6.0 until then. The [tooling topic](/programming/typescript/tooling/) is where the compiler and its settings live. ## Is TypeScript Hard to Learn? The first layer of TypeScript is gentle and quick to pick up. You name a shape, you fix the red squiggle, and your editor starts finishing your sentences. Most people are productive in that layer quickly (and yes, the squiggles are annoying at first, I get it). The second layer is where people stall, because generics and utility types look like algebra. Skip them until you catch yourself copying the same helper for users, then orders, then invoices. The third layer, conditional and mapped types, is powerful and rarely where your first month of value lives. | Layer | Learn it when | Guide | | Annotations and unions | You write your first typed file | [First types](/programming/typescript/types/first-types/) | | Generics and utility types | The same helper keeps getting copied | [Generics topic](/programming/typescript/generics/) | Compiler error messages are the other hard part, since they can run to several lines. Reading them gets much easier once the shapes in the [types topic](/programming/typescript/types/) feel familiar. ## Is TypeScript Worth It with AI Agents Writing Code? Yes, and the case is stronger than it was two years ago. An agent can produce typed-looking code in seconds, which is exactly why you want something mechanical checking it. GitHub's Octoverse report cites a 2025 academic study finding that 94% of LLM-generated compilation errors were type-check failures, meaning the checker is aimed at the most common way generated code breaks. It will not do the review for you. In the 2025 Stack Overflow survey, 84% of respondents were using or planning to use AI tools, yet 46% said they distrust the accuracy of the output and only 33% trust it. The biggest single frustration, at 66%, was answers that are almost right but not quite. What I like about types in that setup is that they turn "almost right" into a red line you can see. What I'd never rely on is a green check as proof, since a compile pass says nothing about whether the function computes the right answer. Here is how to work with an assistant so the types do their job. - Name the types before you ask for the code: give it the data shape, the allowed states, the runtime boundary, and the strictness you are working under. - Read the type model first and the implementation second, because mushy types usually mean a mushy function. - Hunt for `any` where `unknown` or a union belongs, and for optional fields standing in for states that should be modeled. - Distrust `as SomeType` on parsed data, since an assertion is a promise and not a validation. - Compare response types with the backend code, because hand-copied ones start drifting on day one. The best AI-generated TypeScript tends to have boring types, and boring is the compliment. For the labor-market side of whether to learn to code at all, our [should you learn to code](/programming/topics/should-you-learn-to-code/) article has the numbers, and the [AI coding tools hub](/ai-coding-tools/) covers which assistants to try. ## Can I Run TypeScript in Node Now that Node Strips Types? Yes, you can, though Node only handles part of the job. Node's own documentation says it will execute TypeScript files that contain only erasable syntax, meaning syntax it can remove without rewriting anything. Type stripping has been on by default since Node 22.18 and 23.6, and it is marked stable from 24.12 and 25.2. | Job | Who does it | Note | | Run a .ts file | Node, by stripping types | No build step for erasable code | | Check your types | tsc or your editor | Node never checks types | | Use enums or decorators | Not Node's stripping mode | Node reports an error | | Apply tsconfig.json settings | tsc only | Node ignores that file | Node documents the unsupported pieces as enum declarations, namespaces that hold runtime code, parameter properties, import aliases, and decorators. Everything else is just types being deleted. That makes Node great for scripts, tools, and small projects, and it means a clean run proves nothing about your types. Keep a separate `tsc --noEmit` step for that. Deno and Bun also run `.ts` files directly, going by their own documentation, and browsers still need plain JavaScript, so something has to remove the types before a page ships. The [Node topic](/programming/javascript/node/) covers running files and projects, and the compiler side lives in the [project workflow guide](/programming/typescript/tooling/project-workflow/). ## Should I Pick TypeScript or Another Language for This Job? Pick the language by the job in front of you. TypeScript is the natural fit when a screen is involved and you already know JavaScript, and other languages win on specific jobs. | Job | Strong pick | Why | | Browser interface | TypeScript | Browsers run JavaScript only | | API next to that interface | TypeScript | One language, shared models | | Simple, fast backend service | Go | Easy deploys, built-in concurrency | | Data or machine learning | Python | The tooling is Python-first | | Memory-critical systems code | Rust | Safety without a garbage collector | | Teams already on .NET | C# | Mature platform and tooling | My call: if you know JavaScript and the project has a screen, stay in TypeScript. Look at [Go](/programming/go/), [Python](/programming/python/), [Rust](/programming/rust/), or [C#](/programming/csharp/) when the job leans toward their strengths. ## What Mistakes Do Beginners Make with TypeScript? Four mistakes come up again and again, and people usually blame the language for them when the model underneath is the real problem. - Treating the compiler as a linter to appease: an error usually means the model is unclear, and a better type or a simpler function often fixes it where another annotation will not. - Reaching for `as` instead of a check: an assertion is a promise and not a validation, as the [runtime data guide](/programming/typescript/types/unknown-any-runtime-data/) shows. - Making every property optional: a pile of `?` fields hides which combinations are legal, and [discriminated unions](/programming/typescript/types/union-types-and-narrowing/) name the states instead. - Letting a shared types file become a dumping ground: keep types near the code they describe until they are really shared, a line the [interfaces and aliases guide](/programming/typescript/types/interfaces-and-type-aliases/) draws. ## What Can't TypeScript Do? TypeScript does not validate values that arrive at runtime. Your program can run its own checks and validators, but TypeScript will not do it for you, so network responses, form input, and environment variables need code you write. It cannot rescue a bad model, since a type can describe a confusing domain perfectly and the domain stays confusing. It does not replace tests, because type checking confirms that shapes fit under your config and says nothing about whether a function gets the right answer. It also gives you no security boundary at all. Anything crossing the wire is untrusted however it is typed, which is why [application security](/appsec/) treats the client as hostile input. ## Where Do I Go Next? Every Guide Under This Page Each group below starts from the problem you are probably having, then lists the guides that fix it in reading order. ### The Type System Start here if values keep arriving in a shape your code did not expect. The four guides build on each other, and the [types topic](/programming/typescript/types/) holds them together. - [The First Types to Learn](/programming/typescript/types/first-types/): primitive annotations, arrays, object types, and where inference already knows the answer. - [Interfaces and Type Aliases](/programming/typescript/types/interfaces-and-type-aliases/): what each form can express, extension, declaration merging, and which one to default to. - [Union Types and Narrowing](/programming/typescript/types/union-types-and-narrowing/): literal types, the narrowing toolkit, discriminated unions, and exhaustiveness with `never`. - [unknown, any, and Runtime Data](/programming/typescript/types/unknown-any-runtime-data/): when `unknown` is the right starting type at a boundary, how to validate parsed JSON, and what an assertion really costs. ### Reusable Types and Generics Start here if you wrote the same function for three different shapes. These guides are about naming a shape once and reusing it, and the [generics topic](/programming/typescript/generics/) shows how they connect. - [Typing Functions and Callbacks](/programming/typescript/generics/typing-functions-and-callbacks/): parameters, return types, optional and rest parameters, and the contextual typing that saves the annotation. - [Generic Functions and Types](/programming/typescript/generics/generic-functions-and-types/): type parameters, inference at the call site, constraints with `extends`, and defaults. - [Utility Types](/programming/typescript/generics/utility-types/): `Pick`, `Omit`, `Partial`, `Record`, `ReturnType`, and when a named type beats a clever one. ### Configuration and Tooling Start here if your editor is green and your pipeline is red. This is the half of TypeScript that sits outside the type system: the compiler, the config file, and two contexts with typing rules of their own, React components and API contracts. All four guides sit under the [tooling topic](/programming/typescript/tooling/). - [tsconfig.json Explained](/programming/typescript/tooling/tsconfig/): which files get checked, what `strict` turns on, the flags that stay off until you name them, and sharing a config. - [Tooling and Project Workflow](/programming/typescript/tooling/project-workflow/): the compiler commands, why transpiling and type checking are separate jobs, a real check in CI, and migrating a JavaScript codebase. - [TypeScript with React](/programming/typescript/tooling/with-react/): JSX settings, prop types, typed state and refs, event handlers, and the server and client boundary. - [API Contracts and Shared Models](/programming/typescript/tooling/api-contracts/): modeling a request and response once, generating types from a schema, and why validation stays a separate step. ## What Should I Learn First? Learn it in roughly the order you will use it at work. 1. Get comfortable with [JavaScript](/programming/javascript/) first: objects, arrays, modules, promises, and closures. 2. Learn annotations, inference, object types, and function signatures. 3. Learn unions and narrowing before you go near generics. 4. Add generics and utility types once duplication actually appears. 5. Read `tsconfig.json` until the main strictness settings make sense. 6. Apply all of it to React props, API clients, and server data. Skip advanced type gymnastics until something forces the issue. Conditional types, mapped types, and template literal types are powerful, and they are rarely where the first month of value lives. ## Which Languages and Comparisons Sit Next to TypeScript? - [JavaScript vs TypeScript](/programming/javascript-vs-typescript/): the full side-by-side, if you are still weighing whether types are worth the step. - [JavaScript](/programming/javascript/): the runtime model TypeScript compiles down to and never replaces. - [Frontend development](/programming/topics/frontend/): the order to learn HTML, CSS, and JavaScript before you add types. - [Go](/programming/go/): a typed backend language when deployment simplicity and concurrency matter more than the browser. - [Rust](/programming/rust/): a stricter compiler still, when memory safety and low-level control are part of the job. - [C#](/programming/csharp/): a mature managed platform when .NET, ASP.NET Core, and enterprise tooling are the environment. - [Programming](/programming/): the wider language index, including the data and database guides. ## Frequently Asked Questions ### Is TypeScript replacing JavaScript? No. TypeScript compiles to JavaScript and runs on the same engines, so it is a layer over the language rather than a successor. A browser build ships JavaScript once the types are removed, though some projects only type-check with noEmit, emit declarations alone, or run .ts files directly in Node, Deno, or Bun. ### Do you need to learn JavaScript before TypeScript? You need the JavaScript fundamentals at the very least. TypeScript adds a type layer on top of JavaScript semantics, so closures, asynchronous behavior, and the way objects and arrays actually work still apply. Learning both at once tends to hide which half is causing a problem. ### Does TypeScript make code run slower? No. The type layer is erased during compilation, so annotations and interfaces add nothing to the shipped JavaScript. Some compiler options do change the output, such as an older target that downlevels syntax or adds helpers, but the everyday cost of type checking lands at build time and in the editor. ### What are .d.ts declaration files for? They describe the shape of code without containing it, so TypeScript can check calls into plain JavaScript libraries, built-ins, and published TypeScript APIs. Many packages ship their own declarations alongside the code. For untyped packages, community definitions often live under npm's @types scope, and where none exists you write the declarations yourself. ### Which section should you start with? Types, unless you already write annotations comfortably. Generics only pays off once you are repeating the same shapes, and tooling makes more sense after strict-mode errors are readable. The at-a-glance table above routes each case. ### Is TypeScript harder than JavaScript? It is more to learn, because you pick up the type layer after the language. The first layer of annotations is gentle, while generics and conditional types take longer. Most beginners do fine by learning JavaScript first and adding types when a project starts to need them. ### Should you learn TypeScript or Python first? Pick by what you want to build. TypeScript fits browser interfaces and full-stack web work, while Python fits scripting, data, and machine learning. If you pick TypeScript, learn plain JavaScript first, since the types sit on top of it. ### Do AI coding agents make TypeScript unnecessary? No. Agents write TypeScript fluently, and the type checker is a cheap first reviewer of what they produce. GitHub's 2025 Octoverse report links TypeScript's rise to typed languages making agent-assisted coding more reliable. You still have to read the types and run real tests. ## Where to Start Build one small thing with real state: a task list, a settings page, or an admin table. Give it a nullable field, a load state modeled as a union, and one API call validated at the boundary. Work through [the first types to learn](/programming/typescript/types/first-types/) if annotations are new, then [unions and narrowing](/programming/typescript/types/union-types-and-narrowing/). --- ## NuGet Packages for ASP.NET Core URL: /programming/csharp/frameworks/aspnet-core-nuget-packages/ Cluster: Programming > Csharp A useful NuGet package list for an ASP.NET Core application begins with the web SDK and adds only the capabilities the application can name and test. The ASP.NET Core shared framework is a stocked shelf: routing, configuration, dependency injection, logging abstractions, data protection, and many other web features already ship together. NuGet supplies the missing tools for a specific database, API contract, integration, or test boundary. ## Start with the Shared Framework A project using `Microsoft.NET.Sdk.Web` implicitly references `Microsoft.AspNetCore.App`. Ordinary web applications therefore start without separate package references for MVC, dependency injection, configuration, localization, data protection, or SignalR. ```xml net10.0 enable ``` Check the target framework before copying any package command. Package families maintained with .NET commonly align their major releases with the target framework, while third-party projects publish their own compatibility guidance. Use the SDK template as the baseline, then inspect the current dependency set before adding anything: ```bash dotnet list package --include-transitive dotnet list package --outdated --include-transitive dotnet list package --vulnerable --include-transitive ``` These commands expose direct and transitive dependencies, available updates, and reported vulnerabilities. Package review still requires release notes and project documentation because a clean restore cannot prove that an integration matches the application. ## Choose Packages by Missing Capability Each package should answer a concrete requirement and have a test that proves the integration works. - `Microsoft.EntityFrameworkCore` plus a provider supports relational models, LINQ queries, change tracking, and migrations. - `Microsoft.AspNetCore.OpenApi` generates OpenAPI documents from ASP.NET Core endpoints. - `Microsoft.AspNetCore.Authentication.JwtBearer` validates bearer tokens issued through OAuth or OpenID Connect systems. - `Microsoft.Extensions.Http.Resilience` adds configured resilience handlers to outbound `HttpClient` calls. - `Microsoft.Extensions.Caching.Hybrid` coordinates local and optional distributed caching with stampede protection. - `OpenTelemetry.Extensions.Hosting` connects .NET telemetry to OpenTelemetry configuration and exporters. - `Serilog.AspNetCore` integrates Serilog request logging and host setup. - `Microsoft.AspNetCore.Mvc.Testing` boots the real web host inside integration tests. Add package names through the CLI without copying stale version pins from an article: ```bash dotnet add src/Store.Api package Microsoft.AspNetCore.OpenApi dotnet add src/Store.Api package Microsoft.EntityFrameworkCore.SqlServer dotnet add tests/Store.Api.Tests package Microsoft.AspNetCore.Mvc.Testing ``` Select the exact compatible release through the project's dependency policy, official compatibility documentation, and package audit process. The project paths keep runtime packages in the web app and the testing package in the integration-test project. ## Data Access and API Contracts EF Core needs a provider for the database the application will use. SQL Server, PostgreSQL, SQLite, and MySQL-family providers differ in SQL translation, data types, migration behavior, and operational tooling. Use EF Core when the application benefits from a relational model, change tracking, and reviewed migrations. Direct SQL or another data-access library can serve applications that need exact query control or have a small, read-heavy query surface. `Microsoft.AspNetCore.OpenApi` fits APIs that need a machine-readable OpenAPI document. A browser UI, client generator, or additional documentation interface is a separate requirement with its own package choice. ```csharp builder.Services.AddOpenApi(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.MapOpenApi(); } ``` This small registration creates the application's document-generation boundary. Add a UI package only when developers or API consumers need that interface. ## Authentication and Outbound Resilience `Microsoft.AspNetCore.Authentication.JwtBearer` fits APIs that receive access tokens from a trusted OAuth or OpenID Connect issuer. Installation is the smallest part of the work because token validation also needs a correct issuer, audience, signature, expiration policy, and authorization design. `Microsoft.Extensions.Http.Resilience` fits services that call remote APIs and need controlled timeouts, retries, or circuit breakers. Configure each resilience policy around the specific remote operation. A read-only catalog lookup may tolerate a retry, while a payment or order command needs an idempotency design before automatic repetition is safe. ## Caching and Observability `Microsoft.Extensions.Caching.Hybrid` fits expensive read paths with stable cache keys and known lifetimes. Define ownership and invalidation before storing user-specific, tenant-specific, or permission-sensitive results. OpenTelemetry packages fit applications that need traces and metrics exported to a collector or monitoring backend. Start with `OpenTelemetry.Extensions.Hosting` and add instrumentation packages only for boundaries the application uses. ASP.NET Core already supports structured `ILogger` messages. Choose `Serilog.AspNetCore` when Serilog's request logging, sinks, enrichment, or existing operational setup provides a required path from application events to the people investigating them. ## Integration Testing The `Microsoft.AspNetCore.Mvc.Testing` package belongs only in a dedicated test project. Its `WebApplicationFactory` can boot routing, middleware, dependency injection, model binding, filters, and authorization with test configuration. ```csharp public sealed class StatusApiTests : IClassFixture> { [Fact] public async Task StatusEndpointReturnsSuccess() { await using var factory = new WebApplicationFactory(); using var client = factory.CreateClient(); var response = await client.GetAsync("/status"); response.EnsureSuccessStatusCode(); } } ``` ```csharp // Store.Api/Program.cs, after app.Run() public partial class Program { } ``` The public partial declaration exposes the top-level `Program` class to the test project. This test catches host wiring failures that an isolated method test cannot reach. Use the real database provider or a close substitute when provider behavior affects the result. ## Packages that Need a Clear Case Several established packages remain useful when their specific tradeoff matches the application. - `AutoMapper` fits broad, repetitive mapping rules with tests and visible configuration. Explicit projections fit small APIs with a few contracts. - `MediatR` fits applications that need a mediator pipeline across many commands or queries. Direct service calls keep smaller applications easier to trace. - `FluentValidation` fits reusable validation rules and its current ASP.NET Core integration guidance. Endpoint-local checks may be clearer for a small Minimal API. - `Newtonsoft.Json` fits compatibility requirements or JSON behaviors unavailable in `System.Text.Json`. - `Swashbuckle.AspNetCore` and `NSwag.AspNetCore` fit UI or client-generation requirements beyond first-party document generation. The decision should name the repetition being removed, the integration owner, and the behavior covered by tests. That standard keeps the stocked shelf usable instead of filling it with duplicate tools. ## Common Pitfalls & Debugging ### The Package Duplicates Framework Code Symptom: the project references a package for a capability already supplied by `Microsoft.AspNetCore.App`. Cause: an older package list was copied into a newer project. Fix: check the shared-framework documentation, remove the duplicate reference, restore, and run integration tests. ### A Package Targets a Different Framework Line Symptom: restore reports compatibility warnings or startup fails after a package change. Cause: the selected package release does not support the project's target framework. Fix: read the package's official compatibility guidance and choose a maintained release for the target framework. ### A Retry Repeats a Command Symptom: an outbound request creates duplicate payments, orders, or messages. Cause: a resilience handler retried a state-changing operation without idempotency protection. Fix: restrict retries to safe operations or add an idempotency key and server-side duplicate detection. ## Frequently Asked Questions ### Is Newtonsoft.Json still needed in a modern ASP.NET Core app? Not by default. System.Text.Json has been the built-in serialiser since ASP.NET Core 3.0 and is faster with fewer allocations. Add Newtonsoft.Json for a specific compatibility need, such as converter patterns it still handles or an existing codebase already built on it. ### How do you check a NuGet package for known vulnerabilities? Run dotnet list package with the vulnerable and include-transitive options, which reports advisories for your packages and everything they pull in. Read a clean result as nothing disclosed rather than proven safe, since it only covers published advisories. ### Do minimal APIs need different packages than controller-based APIs? Mostly no. Both run on the same framework and share the same choices for data access, resilience, caching, and testing. The difference is narrow. Some MVC-oriented tooling targets controllers, while minimal APIs commonly pair with Microsoft.AspNetCore.OpenApi for documentation. ### What is the difference between dotnet add package and editing the csproj? The end state is the same PackageReference entry. The command resolves and writes the version for you; editing by hand gives control over exact pins and metadata such as PrivateAssets, at the cost of supplying the version yourself and running a restore afterwards. ## Package Review Checklist 1. Confirm the shared framework does not already supply the capability. 2. Name the application requirement and the project that owns the dependency. 3. Verify target-framework compatibility in official package documentation. 4. Review maintenance, licensing, transitive dependencies, and security advisories. 5. Add an integration test that exercises the package boundary. 6. Record the package's upgrade owner and remove it when the requirement disappears. ## Next Steps Begin with the web SDK, add one package for one verified requirement, and test the resulting application boundary. Continue with the [C# frameworks guide](/programming/csharp/frameworks/) for request-pipeline, EF Core, testing, worker, and deployment decisions. Use the [SQL programming guide](/programming/sql/) before choosing a database provider, because the database workload should determine the provider and persistence design. --- ## C# Frameworks: .NET, ASP.NET Core, and EF Core URL: /programming/csharp/frameworks/ Cluster: Programming > Csharp .NET provides the runtime and developer tools for [C# applications](/programming/csharp/), while ASP.NET Core handles web requests and Entity Framework Core handles relational data when an object-relational mapper fits the project. Think of the stack as a workshop with a shared workbench. .NET is the workbench, ASP.NET Core organizes HTTP work, EF Core handles a specific class of database tasks, and NuGet supplies extra tools only when the standard set lacks a required capability. The strongest default for a new backend is a small ASP.NET Core application with an explicit request pipeline, few dependencies, integration tests, and a deployment plan tied to a supported .NET release. ## How the C# Framework Stack Fits Together Each layer solves a different problem, so framework selection starts by naming the job the application must perform. | Layer | Primary job | Use it for | | .NET | Runtime and SDK | All C# application types | | ASP.NET Core | HTTP application framework | Web apps and APIs | | EF Core | Relational data mapping | LINQ and migrations | | NuGet | Package distribution | Missing application capabilities | A command-line tool may need only the .NET SDK. A small API may use ASP.NET Core with direct SQL. A database-backed product may use all four layers because the application needs HTTP endpoints, migrations, a relational model, and a few focused packages. Use a supported target framework and keep the SDK patched in development and CI. Major .NET versions can install side by side, so the project file remains the clearest record of the runtime the application expects. ```xml net10.0 enable enable ``` Nullable reference types expose missing-data risks during compilation. The web SDK also supplies the ASP.NET Core shared framework, which prevents ordinary web projects from needing separate references for every built-in web capability. ## Choose .NET for the Project Constraints .NET is strongest when an application benefits from static typing, long support windows, a mature debugger, and one toolchain across local development, CI, tests, and publishing. The strongest project candidates share several practical constraints: - Long-lived HTTP APIs with many typed request and response contracts. - Database-backed products that need migrations and transaction boundaries. - Internal services integrated with Microsoft Entra ID or Azure infrastructure. - Background workers that share C# libraries with a web application. - Teams maintaining existing C# systems and operational knowledge. A short script, browser-only interface, or tiny edge function may carry more runtime and build structure than the work needs. Python, JavaScript, or a platform-native function can provide a shorter path for those projects. Choose from the expected maintenance work as well as the first demo. A project that will gain authentication, database migrations, scheduled jobs, and several API integrations can use the structure early. A disposable conversion script benefits more from a small executable surface. ## Choose the ASP.NET Core Project Style ASP.NET Core supports several application styles on the same runtime. Choose from the size of the HTTP surface and the kind of user interface the project needs. - Minimal APIs fit small APIs and internal services whose handlers stay short. - Controllers fit larger APIs that use filters, conventions, and shared request policies. - Razor Pages fit server-rendered forms and page-oriented back-office tools. - Blazor fits interactive C# user interfaces after the hosting model has been evaluated. Minimal APIs offer the cleanest starting point for learning the request path. Move application rules into services as soon as an endpoint begins coordinating validation, persistence, authorization, and external calls. ## Build the Request Pipeline First Modern ASP.NET Core applications register services, build the host, add middleware, and map endpoints in `Program.cs`. Middleware order controls behavior because every request travels through that ordered pipeline. ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddScoped(); var app = builder.Build(); app.UseHttpsRedirection(); app.MapGet("/products", async ( IProductCatalog catalog, CancellationToken cancellationToken) => { var products = await catalog.FindActiveAsync(cancellationToken); return Results.Ok(products); }); app.Run(); public partial class Program { } ``` The service container owns `IProductCatalog`, while the endpoint owns the HTTP response. Cancellation flows into the service so a disconnected request or application shutdown can stop database and network work that no longer has a caller. Keep the pipeline readable from top to bottom. Authentication must run before authorization, exception handling must wrap the code it protects, and endpoint mapping must happen before the application starts. ## Add EF Core When the Data Model Needs It EF Core maps C# types to relational tables, translates LINQ into provider-specific SQL, tracks changes, and creates schema migrations. It fits product applications that benefit from a persistent domain model and reviewed schema history. EF Core does not replace schema review, query plans, or database constraints. ```csharp namespace Store.Api; public sealed class StoreDbContext : DbContext { public StoreDbContext(DbContextOptions options) : base(options) { } public DbSet Products => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity(entity => { entity.HasKey(product => product.Id); entity.Property(product => product.Name) .HasMaxLength(200) .IsRequired(); }); } } ``` Keep concrete database knowledge alongside the application model. Column types, unique constraints, indexes, transaction boundaries, and provider behavior still decide whether the application preserves data correctly. Inspect generated SQL when a query becomes slow, and review every migration before production. ```bash dotnet tool install --global dotnet-ef dotnet add package Microsoft.EntityFrameworkCore.Design dotnet ef migrations add AddProducts dotnet ef database update ``` Applications dominated by reporting SQL, bulk operations, or provider-specific queries may use direct SQL or a smaller data-access layer. The selection criterion is control over the real database workload. ## Keep NuGet Dependencies Deliberate NuGet extends the application when the shared framework lacks a required capability. A database provider, an OpenTelemetry exporter, or a test host has a clear job; a copied package list usually carries stale assumptions. ```bash dotnet list package dotnet list package --outdated dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet remove package Unused.Dependency ``` Review every package addition for maintenance, target-framework compatibility, transitive dependencies, licensing, and security advisories. Keep test-only packages inside test projects, and remove packages whose capability has moved into the shared framework. The [ASP.NET Core NuGet package guide](/programming/csharp/frameworks/aspnet-core-nuget-packages/) applies those criteria to common package families. ## Separate Authentication from Authorization Authentication identifies the caller, while authorization decides which resources and operations that identity can access. Cookies, bearer tokens, OpenID Connect, API keys, and machine credentials have different threat models. An API accepting OAuth or OpenID Connect access tokens can use `Microsoft.AspNetCore.Authentication.JwtBearer`. Configuration must validate the token issuer, audience, signature, and expiration before the application trusts its claims. Authorization policies should express application rules such as tenant membership or permission to edit a product. A check for an authenticated identity only proves that a caller signed in; it leaves the resource decision unresolved. Keep production secrets outside source files and deployment artifacts. ASP.NET Core configuration can combine local JSON defaults with environment variables and a platform secret store, while environment checks keep developer exception pages and permissive test settings away from production. ## Test the Application that Will Run Unit tests fit isolated business rules with few external dependencies. Integration tests fit routing, middleware, service registration, authentication policies, JSON contracts, and database-provider behavior because those risks appear only when the application boots. ```csharp public sealed class ProductApiTests : IClassFixture> { private readonly HttpClient _client; public ProductApiTests(WebApplicationFactory factory) { _client = factory.CreateClient(); } [Fact] public async Task ProductsEndpointReturnsSuccess() { var response = await _client.GetAsync("/products"); response.EnsureSuccessStatusCode(); } } ``` `WebApplicationFactory` exercises the real host with test configuration. The `public partial class Program {}` declaration after `app.Run()` exposes the top-level entry point to the test project. Database tests should use the production provider or a close substitute when constraints, transactions, migrations, generated columns, or provider functions affect correctness. ## Design Background and Outbound Work Hosted services support short background tasks inside an ASP.NET Core process. Dedicated worker projects suit queue consumers and jobs that need separate scaling, longer retry policies, or independent deployment. A background service must pass its shutdown token into delays, database calls, and external requests. It should create a dependency-injection scope before resolving scoped services such as a `DbContext`. Outbound HTTP calls need similarly explicit lifetime and failure ownership. Use long-lived clients or `IHttpClientFactory`, configure timeouts, and retry only operations that are safe to repeat. Payment commands and order submissions need idempotency before an automatic retry can be considered safe. ## Make Production Behavior Visible ASP.NET Core provides structured logging through `ILogger`, and .NET exposes first-party hooks for metrics and traces. OpenTelemetry can export those signals to a collector or monitoring backend. ```csharp _logger.LogInformation( "Completed product import for batch {BatchId}", batchId); ``` Structured placeholders keep `BatchId` queryable as data. Exclude tokens, cookies, connection strings, secrets, and unfiltered request bodies from logs. A production service needs enough evidence to answer which endpoint failed, which dependency slowed down, and which deployment changed the behavior. Instrument the HTTP pipeline, database boundary, and outbound calls that decide the user-visible result. ## Publish with a Runtime Plan The `dotnet publish` command creates the application output that a host or container will run. Build the release configuration in CI so the deployable artifact comes from the same tested source state. ```bash dotnet test --configuration Release dotnet publish Store.Api --configuration Release --output ./publish ``` A framework-dependent deployment expects a compatible runtime on the host. A self-contained deployment includes the selected runtime, which increases the artifact and moves runtime patching into the application's release process. Containers also need base-image updates because an old runtime remains old inside a reproducible image. Record four operational decisions before production: who patches the runtime, how migrations are applied, where logs and traces are collected, and how the previous release is restored. Health checks should cover the service process without turning every dependency outage into an automatic restart loop. ## Use a Project Layout with Clear Owners A single project is enough while the application stays small. Split projects when separate responsibilities begin changing for separate reasons. ```text src/ Store.Api/ Store.Application/ Store.Infrastructure/ tests/ Store.Api.Tests/ Store.Application.Tests/ ``` The API project owns HTTP contracts and framework wiring. The application project owns use cases and business rules. Infrastructure owns EF Core, queues, file storage, and external service clients. This layout is useful only when those boundaries reduce coupling in real changes. ## Common Pitfalls & Debugging ### Middleware Runs in the Wrong Order Symptom: authorization fails unexpectedly or errors bypass the configured handler. Cause: middleware was registered after the component it must protect. Fix: read `Program.cs` in request order, place exception handling early, and keep authentication before authorization. ### EF Core Generates an Expensive Query Symptom: a page triggers many database calls or scans more rows than expected. Cause: hidden lazy loading, an unbounded query, or a missing index changed the SQL workload. Fix: inspect generated SQL, project only required columns, bound the result, and check the database plan. ### A Background Service Fails at Startup Symptom: the host reports a lifetime error for `DbContext` or another scoped service. Cause: a singleton hosted service requested a scoped dependency from its constructor. Fix: inject `IServiceScopeFactory`, create a scope for each unit of work, and dispose it promptly. ## Frequently Asked Questions ### Is .NET only for Windows applications? .NET runs on Windows, Linux, and macOS, and ASP.NET Core services commonly run in Linux containers. Windows remains a strong fit for applications tied to Microsoft infrastructure, while the runtime itself supports cross-platform deployment. ### Should a new API use Minimal APIs or controllers? Minimal APIs suit small endpoint sets with direct service boundaries. Controllers suit larger APIs that benefit from filters, conventions, model binding policies, or an established MVC structure. Start with the smallest style that keeps handlers readable. ### Does every ASP.NET Core app need EF Core? No. EF Core suits applications that benefit from LINQ queries, migrations, change tracking, and provider integration. Direct SQL or another data-access library can fit reporting services, small query surfaces, or workloads that need precise SQL control. ### When should a web app use a separate worker? Use a separate worker when background work needs independent scaling, queue consumption, long retries, or deployment controls. A short cleanup task can stay in the web process when shutdown, failure, and resource use remain predictable. ### When should middleware be added to the request pipeline? Add middleware when matching requests need the same cross-cutting behavior, such as exception handling, HTTPS redirection, authentication, or authorization. Place it before the endpoints or middleware it must wrap, and keep endpoint-specific rules in the endpoint when that owner is clearer. ### How should EF Core migrations reach production? Generate migrations from reviewed model changes, inspect the SQL they produce, and apply them through one controlled deployment step. Avoid letting every application instance race to migrate the same production database during startup. ## Self-Check 1. Multiple choice: Which layer handles the ASP.NET Core HTTP request pipeline: .NET, ASP.NET Core, or EF Core? 2. Multiple choice: Which project style best fits a page-oriented server-rendered admin tool: Minimal APIs, Razor Pages, or a worker service? 3. Multiple choice: Where should `Microsoft.AspNetCore.Mvc.Testing` be referenced: the production API project or the integration-test project? 4. Predict the result: What response body does `app.MapGet("/status", () => Results.Ok("ready"));` return for a successful request? 5. Predict the result: What happens when a singleton hosted service requests a scoped `DbContext` directly from its constructor while scope validation is enabled? ### Answers 1. ASP.NET Core. .NET supplies the runtime, while ASP.NET Core owns web middleware and endpoint routing. 2. Razor Pages. Its page-oriented model fits server-rendered forms and back-office screens. 3. The integration-test project. The package boots the application for tests and does not belong in the production dependency set. 4. `ready`. The endpoint returns an HTTP 200 response whose JSON string value is `"ready"`. 5. The host fails validation or startup. Create a scope inside the worker before resolving the scoped database context. ## Next Steps Start with a small API, one real database operation, and an integration test that boots the host. Add more framework features only when your application needs them. Continue with the [ASP.NET Core NuGet package guide](/programming/csharp/frameworks/aspnet-core-nuget-packages/), then use the [SQL programming guide](/programming/sql/) to review schema design, joins, indexes, and transactions beneath the data-access layer. --- ## CSS Display and Normal Flow: Block, Inline, Inline-Block URL: /programming/css/layout/display-and-flow/ Cluster: Programming > Css Normal flow is the browser's default way of arranging boxes before another layout rule changes them. A document with headings and paragraphs remains readable without custom CSS because block content follows the document order and inline content wraps inside each line. For the broader styling basics, start with the [guide to CSS](/programming/css/). This guide is part of our [CSS layout guide](/programming/css/layout/) series. Think of the layout as a printed page. A paragraph begins as a new block, words run inline across its lines, and a small label can sit inside a line while keeping its own rectangular size. The `display` property tells the browser which of those page roles a box should use. ## What Is Normal Flow in CSS? Normal flow lays out block boxes in document order and inline content within lines. The display property can change how a box sits among its siblings and how its children are arranged. Those are its outer and inner display types. ```css display: block; display: inline; display: inline-block; ``` The browser’s default stylesheet supplies useful display values for common HTML elements. Paragraphs, headings, lists, and `div` elements normally generate block boxes. Links, `span` elements, `strong`, and `em` normally generate inline boxes. CSS can change the display type without changing the element's semantic meaning. Normal flow also preserves the document's source order. A later block follows the earlier block, and text wraps when the available inline space runs out. Flexbox, grid, floats, and positioning modify parts of that arrangement, so debugging starts by identifying what normal flow would have done. ## How Do CSS Block, Inline, and Inline-block Differ? In CSS normal flow, a block box starts a new line, an inline text box shares the surrounding line, and an inline-block shares the line while accepting a width and height. Try each display value on a notice, highlighted phrase, or tag to see how much space it takes. ### Block Starts a New Line ```css .notice { display: block; width: 18rem; padding: 1rem; margin-block: 1rem; } ``` The notice begins on a new line. Its declared width is respected, and the vertical padding plus margins create space around the full rectangle. Without the explicit width, the block's outer box normally expands across the available width of its containing block. ### Inline Stays with the Text ```css .highlight { display: inline; width: 10rem; padding-inline: 0.25rem; background: #dcebe5; } ``` The highlighted words remain inside the sentence and can wrap across lines. Horizontal padding moves neighboring text away from the painted background. The browser ignores the ordinary `width: 10rem` because a non-replaced inline box (an inline box containing text rather than embedded content such as an image) takes its width from the text fragments inside the line. ### Inline-block Stays Inline and Accepts a Size ```css .tag { display: inline-block; width: 6rem; padding: 0.35rem 0.5rem; text-align: center; } ``` The tag sits beside surrounding text like another inline item, while its contents use a block-style rectangle. The browser now respects the width and vertical padding. This combination suits a compact badge or label that belongs in a text line but needs predictable dimensions. The printed-page model still holds for all three. Block begins a new region, inline joins the words already running across the line, and inline-block places a measured label within that same line. Choose the role from the space the element should claim. ## Common Pitfalls & Debugging ### Width Has No Effect on an Inline Box A width or height declaration appears in developer tools but the element does not change size. The element is a non-replaced inline box. Use `inline-block` when the element should stay in the text line, or `block` when it should start a new line. ### Vertical Margins Collapse Two stacked blocks with vertical margins produce less space than adding both values suggests. Adjoining vertical margins in block flow can collapse into one margin, usually using the larger positive value. Set the intended spacing on one side, or create a new formatting context when the container's layout requires separate margins. ```css .section { display: flow-root; } .section > * { margin-block: 0; } .section > * + * { margin-block-start: 1rem; } ``` `flow-root` creates a new block formatting context, which stops the section's margins from collapsing with child margins through the container edge. Adjacent child margins can still collapse with each other. Resetting those margins lets the adjacent-sibling rule own the spacing in one predictable place. ### Inline Padding Overlaps Nearby Lines An inline highlight's background or border paints over text in the line above or below. Vertical padding paints around the inline box without increasing the line box spacing in the same way a block dimension would. Use `inline-block` for a self-contained label, or adjust the surrounding line height when the effect belongs to ordinary text. ## Choose the Display Value That Fits Normal flow gives every box a useful starting place. Use block for a new region, inline for text fragments, and inline-block for a measured box that must remain inside the line. ## Frequently Asked Questions ### What is normal flow in CSS? Normal flow is the browser's default system for arranging boxes before floats, positioning, flexbox, or grid change their layout. Block boxes follow one another in the block direction, while inline boxes flow together inside line boxes. ### What is the difference between CSS block, inline, and inline-block? A CSS block box starts on a new line and normally fills the available width. A non-replaced inline box, such as a text span, stays in the text line and ignores width and height. An inline-block box stays in the line but accepts dimensions. Inline images are an exception because their dimensions can be set. ### Do margins collapse in flexbox or grid? Margins between CSS flex items or grid items do not collapse. Margin collapsing applies to adjoining margins in block flow. Use gap when you want consistent spacing between flex or grid items. ### Does display: none remove an element from flow the same way position: absolute does? CSS display: none generates no layout box for the element or its descendants. position: absolute removes a box from normal flow but still displays it, positioned against its containing block. ### What is the difference between display: none and visibility: hidden? CSS display: none removes an element’s layout box and its space. visibility: hidden hides the element while preserving its box and space. Both normally hide the affected content from assistive technology, so neither is suitable for a visually hidden accessible label. --- ## CSS Flexbox: One-Dimensional Layout URL: /programming/css/layout/flexbox/ Cluster: Programming > Css CSS flexbox arranges a container's direct children along one main axis and aligns them along a cross axis. It handles navigation bars, button groups, card rows, form controls, and other layouts where content must share space in one direction. For the broader styling basics, start with the [CSS fundamentals](/programming/css/). This guide is part of our [CSS layout guide](/programming/css/layout/) series. Picture a shelf whose books can change width. The shelf is the flex container, and its direct children are flex items. Container rules choose the shelf direction and alignment. Item rules decide which books can grow, which can shrink, and the size each one starts from. ## What Does CSS Flexbox Control? CSS flexbox controls the size, spacing, and alignment of a container’s direct children along a main axis and a cross axis. Set `display: flex` on the parent to make those children flex items. Their own contents keep their existing layout. The main axis always follows the current `flex-direction` value. With `row`, it normally runs in the inline direction. With `column`, it runs in the block direction. The cross axis always runs perpendicular to the main axis, so changing direction also changes the physical direction controlled by the alignment properties. Flexbox resolves positive and negative free space. When room remains on the shelf, `flex-grow` decides how items share it. When the items need more room than the container offers, `flex-shrink` decides how the deficit is removed. `flex-basis` supplies the starting main size before either calculation. ## Build a Flex Layout Step by Step This sequence keeps one topic-card example and adds one flexbox decision at each step. Replace the earlier CSS block as you proceed so the rendered change stays visible. ```html
Selectors
Flexbox with a longer title
Grid
Positioning
``` ### Turn on Flexbox ```css .topic-list { display: flex; } ``` The section becomes a flex container after this declaration. Its four articles become flex items and move into a single row because the initial direction is `row`. The items keep content-based widths, and the browser allows them to shrink when the row runs short of space. ### Choose the Flex Direction ```css .topic-list { display: flex; flex-direction: row; } ``` The explicit row keeps the main axis running across the container. Change the value to `column` and each card stacks vertically because the main axis turns downward. The shelf has rotated, so later main-axis alignment rotates with it. ### Justify and Align the Items ```css .topic-list { display: flex; flex-direction: row; justify-content: center; align-items: stretch; } ``` `justify-content: center` groups the cards in the middle of the row when positive free space remains. `align-items: stretch` lets the cards fill the flex line's cross size, so cards with short labels can match the height of the card with a longer title. If the direction changes to `column`, justification works vertically and alignment works horizontally. Property names describe the flex axes rather than fixed screen directions. Set `align-self` on one flex item to give it different cross-axis alignment from the others. If wrapping creates several lines, `align-content` distributes those lines when the container has spare cross-axis space; `align-items` still aligns items within each line. ### Add a Gap Between Items ```css .topic-list { display: flex; flex-direction: row; justify-content: center; align-items: stretch; gap: 1rem; } ``` The items remain centered while one rem of space appears between each pair. A container gap does not add space at the outer edges, and it keeps spacing separate from a card's own margins. When cards wrap later, the same gap also separates the flex lines. ### Allow Items to Wrap ```css .topic-list { display: flex; flex-direction: row; justify-content: center; align-items: stretch; gap: 1rem; flex-wrap: wrap; } ``` `flex-wrap: wrap` permits items to move onto another line when they cannot fit. Each new line distributes its free space independently, which is a defining flexbox behavior. Cards in separate lines do not share column tracks, even when their widths happen to match. ### Control Grow, Shrink, and Basis ```css .topic-list { display: flex; flex-direction: row; justify-content: center; align-items: stretch; gap: 1rem; flex-wrap: wrap; } .topic-card { flex: 1 1 14rem; min-width: 0; } ``` The shorthand reads as `flex-grow: 1`, `flex-shrink: 1`, and `flex-basis: 14rem`. Each card starts from a preferred main size of `14rem`. Cards on the same line grow to share leftover room, can shrink when necessary, and wrap once the container cannot fit another reasonable card. The `min-width: 0` declaration lets the flex item shrink below its automatic minimum width. Its contents still need somewhere to go. Use wrapping for long words or scrolling for a code block so the content does not spill outside the smaller item. ## Common Pitfalls & Debugging ### Alignment Follows the Current Axes `justify-content: center` moves items vertically when horizontal centering was expected. The container uses `flex-direction: column`, so the main axis is vertical. Identify the current main axis, then use `justify-content` for that axis and `align-items` for the cross axis. ### Items Overflow Because of Min-width A flex child pushes beyond the container even though shrinking is enabled. Flex items default to an automatic minimum size, and long unbroken content can set a large min-content width. Apply `min-width: 0` to the shrinking flex item, then handle the content with wrapping or overflow rules appropriate to that content. ```css .main-panel { flex: 1 1 auto; min-width: 0; } .main-panel pre { overflow-x: auto; } ``` The panel may now shrink within the row, while the code block scrolls internally if a line cannot wrap. Apply the minimum-size fix to the flex item that participates in sizing, which may be a wrapper around the visibly overflowing element. ### Flex: 1 Uses a Zero Basis Several items become equal widths even though their content sizes differ. Browsers expand `flex: 1` to a grow factor of one, a shrink factor of one, and a basis of `0%`. Use `flex: 1 1 auto` when content or an authored width should contribute to the starting size, or write an explicit basis such as `14rem`. Avoid using `order` or reversed flex directions to repair confusing HTML order. They change the visual arrangement, while ordinary keyboard navigation and screen readers still follow the document order. ## When Should You Use Grid Instead of Flexbox? Use CSS grid instead of flexbox when items across several rows need shared column boundaries. Flexbox lays out each wrapped line independently, which suits content-driven rows but does not keep columns aligned between lines. Use [CSS grid](/programming/css/layout/grid/) when the layout needs shared row and column tracks, such as a dashboard or a gallery with aligned columns. A page can use grid for its outer structure and flexbox inside a toolbar, navigation region, or card header. ## Start with the Main Axis Start with the container's axes, then add alignment and wrapping before tuning item flexibility. When overflow appears, inspect the flex item's automatic minimum size before changing widths across the whole layout. ## Frequently Asked Questions ### How do justify-content and align-items differ in CSS flexbox? In CSS flexbox, justify-content distributes free space along the main axis. align-items aligns items across the cross axis within each flex line. Changing flex-direction changes which physical direction each axis follows. ### Why do flex items shrink even when a width is set? Flex items use flex-shrink: 1 by default, so the flex algorithm can reduce their main size when the container lacks space. Set an appropriate flex value, allow wrapping, or use flex-shrink: 0 only when the item must keep its declared size. ### What does flex: 1 1 auto mean? CSS flex: 1 1 auto lets an item grow and shrink from its automatic starting size. The three values set flex-grow, flex-shrink, and flex-basis. With an auto basis, the starting size comes from the main-size property when set, or from the content. ### How do you center an item horizontally and vertically with flexbox? To center an item with CSS flexbox, set display: flex on its container, then set justify-content: center and align-items: center. The container needs spare space along an axis for centering to move the item on that axis. --- ## CSS Grid: Two-Dimensional Layout URL: /programming/css/layout/grid/ Cluster: Programming > Css CSS grid arranges a container's direct children against shared row and column tracks. It suits layouts where items need to line up in two dimensions, including page shells, dashboards, card galleries, and forms with repeated label columns. For the broader styling basics, start with the [CSS learning guide](/programming/css/). This guide is part of our [CSS layout guide](/programming/css/layout/) series. Picture a sheet of graph paper placed inside the container. Grid properties draw the track lines, `gap` opens space between them, and placement rules tell an item which lines it should span. The browser can add more paper when content needs tracks you did not define. ## What Does CSS Grid Control? CSS grid controls rows and columns together. A grid container defines tracks, and its direct children become grid items. The browser places items automatically unless you set their grid positions yourself. The explicit grid contains tracks declared by `grid-template-columns` and `grid-template-rows`. The implicit grid contains tracks the browser creates when extra content or out-of-range placement needs more room. That distinction matters when an unexpected row or column appears. Track sizes can use fixed lengths, content-based keywords, percentages, or the `fr` unit. An `fr` value receives a share of available space after fixed tracks and gaps have been accounted for, which makes it useful for coordinated columns. ## Build a Grid Step by Step The following sequence uses one card container throughout. Each CSS block includes the previous step, so you can replace the earlier rule and observe one layout change at a time. ```html
Release notes
Layout guide
Selector guide
Color guide
``` ### Turn the Container into a Grid ```css .card-grid { display: grid; } ``` The section becomes a grid container, and each article becomes a grid item. With no template columns, auto-placement creates a single column and enough implicit rows to hold the four cards. The result still looks like a vertical stack, but the cards now participate in grid layout. ### Define the Column Tracks ```css .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); } ``` With this short card content, the graph paper has three columns that divide the available space equally. The first three cards fill the first row, and the fourth card starts an automatically created second row. `repeat(3, 1fr)` is the shorter form of `1fr 1fr 1fr`. A bare `1fr` has an automatic minimum, equivalent to `minmax(auto, 1fr)`, so long content can widen a track or overflow. Use `minmax(0, 1fr)` when equal columns must not depend on their content. ### Add Space Between the Tracks ```css .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; } ``` The cards keep their three-column placement while one rem of space appears between every row and column. The gap belongs to the container, so the outer edges remain free of the doubled or missing margins that often appear when each card manages its own spacing. ### Span an Item Across Tracks ```css .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; } .feature { grid-column: 1 / -1; } ``` The feature card starts at the first explicit column line and ends at the last one, so it spans the full first row. The other three cards move to the next row and occupy one column each. Negative line number `-1` means the final explicit grid line, which keeps the span correct if the template later gains another column. ### Make the Columns Responsive ```css .card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: 1rem; } .feature { grid-column: 1 / -1; } ``` `auto-fit` creates as many tracks as fit, while `minmax()` lets each track grow from a preferred minimum of `14rem` to one share of the available space. The nested `min()` allows a track to shrink to the container width when the container is narrower than `14rem`. Wide containers can display several columns at once. As the container narrows, cards move to fewer columns without a media query. The feature still spans from the first to final explicit line, so it stays full width throughout the change. ## The Second Axis: Rows and Alignment Everything above sizes columns, which is the half of grid that overlaps with flexbox. The other half is what makes grid two-dimensional: rows you declare, and alignment that works in both directions at once. ```css .layout { display: grid; grid-template-rows: auto 1fr auto; min-height: 100dvh; } ``` One column, three row tracks sized independently: a header that takes the height its content needs, a main region that absorbs whatever is left over, and a footer sized to its own content. That is the sticky-footer problem solved in one declaration, with no calculation and no absolute positioning. Add a `grid-template-columns` line and the same container becomes a two-dimensional shell, which is the step flexbox cannot take. Rows you do not declare are created for you, and those implicit rows size to their content unless told otherwise. When a grid receives an unknown number of items, `grid-auto-rows` is what gives them a consistent height: ```css .gallery { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 12rem; } ``` Alignment then works on two levels, and mixing them up is a common source of confusion. `justify-items` and `align-items` position each item inside its own cell, with `place-items` as the shorthand for both. `justify-content` and `align-content` position the whole set of tracks inside the container, which only has a visible effect when the tracks do not already fill it. ```css .tiles { display: grid; grid-template-columns: repeat(2, 8rem); place-items: center; /* each item, inside its cell */ place-content: center; /* the tracks, inside the container */ } ``` If grid alignment appears to do nothing, check how much space is available. The content-alignment properties need spare space around the tracks. Item alignment needs room inside the cell; an item with an explicit size equal to its cell has nowhere to move. ## Naming Areas Instead of Counting Lines Placing items by line number works, and it becomes hard to read the moment a layout changes. Grid offers an alternative that no other layout system has: draw the layout as text. ```css .page { display: grid; grid-template-columns: 16rem 1fr; grid-template-areas: "sidebar header" "sidebar main" "sidebar footer"; } .page > header { grid-area: header; } .page > nav { grid-area: sidebar; } .page > main { grid-area: main; } .page > footer { grid-area: footer; } ``` Each quoted string is one row and each word is one cell. Repeating a name across cells makes that area span them, which is why `sidebar` occupies the full height here without any span arithmetic. A single `.` in place of a name leaves a cell deliberately empty. The advantage arrives when the layout has to change. To move the sidebar to the right, rewrite the area strings and change `grid-template-columns` to `1fr 16rem` so the sidebar keeps its width. Stacking everything for a narrow screen needs the column template changed as well, since the areas have to describe a single column: ```css @media (width < 48rem) { .page { grid-template-columns: 1fr; grid-template-areas: "header" "sidebar" "main" "footer"; } } ``` What does not change in either case is the items themselves. Each one still declares `grid-area` by name, so nothing has to be rewritten to sit between different line numbers. Two constraints the syntax enforces are worth knowing before a template silently stops working. Every named area must be rectangular, so an L-shaped region is not expressible. And every row string must declare the same number of cells, because an uneven count makes the whole declaration invalid rather than partially applied. ## Common Pitfalls & Debugging ### Implicit Tracks Appear Unexpectedly An item creates a new column outside the template or an extra row has a surprising size. The item was placed beyond the explicit grid, so the browser created an implicit track. Correct the placement line or set `grid-auto-rows` and `grid-auto-columns` when implicit tracks are intentional. ### Percentage Tracks Overflow with Gap Two `50%` columns plus a gap extend past the container. Each percentage uses the container width, and the gap is added after those tracks. Use `repeat(2, minmax(0, 1fr))` so grid subtracts the gap and the tracks can shrink below their content-based minimum. ```css .two-column { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; } ``` The two tracks now divide the space left after the one-rem gap. Their zero minimum lets each track shrink within the container when its content is wider. ### Grid Is Used for a One-dimensional Row A toolbar needs several placement rules whenever labels change width. Grid tracks are coordinating a layout whose main problem is distributing items along one row. Move one-dimensional rows to [CSS flexbox](/programming/css/layout/flexbox/). A nested grid normally has its own tracks. Use `grid-template-columns: subgrid` on a grid item that also has `display: grid` when its children need to share the parent’s column tracks. The item must span the columns you want it to reuse. Rows can use `subgrid` in the same way. ## Start with the Grid Tracks Define the track system first, then add spacing and item placement only where the content needs them. Responsive `auto-fit` tracks let the graph paper redraw itself as the container changes width. ## Frequently Asked Questions ### How do you place an item in a specific CSS grid cell? Place a CSS grid item with grid-column and grid-row, or give it a named grid-area defined by the container. These properties change its visual position, so keep the HTML order suitable for reading and keyboard navigation. ### What does 1fr mean in CSS grid? The fr unit represents a fraction of the available space after fixed track sizes, gaps, and automatic track minimums are accounted for. Three 1fr columns receive equal shares of the remaining space, but long content can still make a track wider. Use minmax(0, 1fr) when content must not affect equality. ### How do auto-fit and auto-fill differ in CSS grid? In CSS grid, auto-fill preserves empty repeated tracks when fewer items are available. auto-fit collapses those empty tracks so occupied flexible tracks can expand. Both calculate how many tracks fit in the available space. ### Does grid placement change the HTML reading order? CSS grid placement changes visual position without changing the HTML order. Screen readers and normal keyboard focus order still follow the document, so keep that order meaningful. --- ## CSS Layout Guide: Flexbox, Grid, and Positioning URL: /programming/css/layout/ Cluster: Programming > Css CSS layout controls where boxes appear, how much space they use, and how they respond when the viewport changes. Browsers begin with normal flow, which places block content in sequence and lets inline content wrap within a line. Flexbox, grid, and positioning change that default behavior for specific layout jobs. This guide is part of our [CSS guide](/programming/css/) series. Start with readable HTML in normal flow, then choose the smallest layout system that expresses the design. Flexbox distributes items along one axis, while grid coordinates rows and columns together. Positioning handles overlays, pinned controls, and other elements that need coordinate-based placement. ## Syntax at a Glance Modern layouts often use grid for the page structure and flexbox inside smaller interface regions. The following block shows both tools without fixed widths or removing content from normal flow: ```css .page-shell { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem; } @media (min-width: 48rem) { .page-shell { grid-template-columns: minmax(12rem, 18rem) minmax(0, 1fr); } } .toolbar { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; } ``` The page shell starts as one column. At a viewport width of 48rem, the media query adds a sidebar and gives the main area the remaining space. Choose that breakpoint by checking when your content has enough room. The toolbar's direct children become flex items, so they align vertically and share the horizontal room. Both containers still participate in the surrounding document flow. ## CSS Layout Topics Each guide below focuses on one layer of the layout system. Read display and flow first if block, inline, and containing boxes are still unfamiliar. - [CSS Display and Flow](/programming/css/layout/display-and-flow/) explains normal flow plus the `block`, `inline`, and `inline-block` display values. - [CSS Flexbox](/programming/css/layout/flexbox/) covers flex containers, main and cross axes, alignment, wrapping, and flexible item sizing. - [CSS Grid](/programming/css/layout/grid/) shows how to define tracks, create responsive columns, add gaps, and place items across rows or columns. - [CSS Positioning](/programming/css/layout/positioning/) explains the `static`, `relative`, `absolute`, `fixed`, and `sticky` values with visible examples. ## Which CSS Layout Method Should You Use? Use normal flow for text, flexbox for a row or column, and grid for aligned rows and columns. Use positioning for overlays or sticky controls. A page can combine these methods instead of forcing every element into the same layout. Use flexbox when the immediate problem follows a row or a column. Navigation links, button groups, and card headers fit this model because their spacing depends mostly on their own content. Use grid when items must align across both dimensions, such as a page shell or gallery with shared column lines. Positioning also works alongside the other layout systems. A grid card can establish a positioning context for an absolute badge, while a flex toolbar can also be sticky. Keep the main structure in flow so that an overlay or pinned element remains a small exception instead of becoming the layout foundation. ## Common Pitfalls - Margin collapse surprises: review [CSS display and flow](/programming/css/layout/display-and-flow/) before compensating with extra padding or offsets. - Mixing floats with flexbox or grid: move structural rows and columns into [CSS flexbox](/programming/css/layout/flexbox/) or [CSS grid](/programming/css/layout/grid/), and reserve floats for text wrapping. - Using absolute positioning as the page layout: keep the structure in flow and use [CSS positioning](/programming/css/layout/positioning/) for small overlays or pinned controls. - Forgetting `min-width: 0` in a flex item: use the [flexbox overflow fix](/programming/css/layout/flexbox/#items-overflow-because-of-min-width) when long content refuses to shrink. ## Frequently Asked Questions ### Should you use CSS flexbox or grid? Use CSS flexbox for a row or column whose items share space. Use CSS grid when rows and columns need to line up together. You can use grid for a page layout and flexbox for controls inside it. ### When should you use CSS positioning? Use CSS positioning for an offset, overlay, fixed control, or sticky heading. Keep ordinary page structure in normal flow, flexbox, or grid so growing text and changing screen sizes do not leave elements overlapping. ### Do floats still matter in modern CSS? Floats still suit their original purpose: placing an image or small callout beside text so the text wraps around it. Flexbox and grid are the current tools for page columns and interface layout. Treat float-based multi-column layouts as legacy CSS. ### Does normal flow stop applying once you use flexbox or grid? Flexbox and grid change how a container lays out its direct children. Normal flow still applies elsewhere, including inside those children unless their own layout rules change it. ### Do CSS flexbox and grid change the box model? CSS flexbox and grid keep the box model of content, padding, border, and margin. They change how items are sized and arranged, and the margins between flex or grid items do not collapse as block margins can. ### Can CSS layout respond to a container rather than the viewport? CSS size container queries let a component respond to the dimensions of a suitable ancestor. Declare that ancestor as a query container, then use @container rules to adapt the component inside differently sized regions. ## MDN Resources - [CSS layout](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout) - [Introduction to CSS layout](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Introduction) - [Basic concepts of flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Flexible_box_layout/Basic_concepts) - [How grid relates to other layout methods](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Grid_layout/Relationship_with_other_layout_methods) --- ## CSS Positioning: Static, Relative, Absolute, Fixed, Sticky URL: /programming/css/layout/positioning/ Cluster: Programming > Css CSS positioning controls how an element participates in normal flow and which box supplies the coordinates for its offsets. Think of normal flow as assigned seating: every element receives a place in order. The `position` value decides whether an element stays in that seat, shifts from it, leaves the row, follows the viewport, or stops moving at a scroll threshold. For the broader styling basics, start with the [CSS basics](/programming/css/). This guide is part of our [CSS layout guide](/programming/css/layout/) series. That seating model makes the five values easier to predict. It also explains why an absolute badge can jump to a page corner and why a sticky heading can stop at the edge of its container. If you learn by doing, jump to [Build a Positioned Card Step by Step](#build-a-positioned-card-step-by-step) first. ## What Does CSS Positioning Change? CSS positioning controls whether an element stays in normal flow and which containing block its offsets use. The `position` property works with physical insets such as `top` and logical insets such as `inset-inline-end`. Choose the positioning mode before changing offsets. `static`, `relative`, and `sticky` keep a place in normal flow. `absolute` and `fixed` remove the element from flow, so nearby content lays out without reserving space for it. This distinction causes most visible positioning differences. ## Position Static Keeps Normal Flow `static` is the default for every element. The browser places the box in its assigned seat, and physical inset properties plus `z-index` have no effect on an ordinary static box. ```css .notice { position: static; top: 2rem; } ``` The notice appears exactly where normal flow placed it. The `top: 2rem` declaration does not move the box because static elements are not positioned elements. ## Position Relative Offsets an Element `relative` keeps the assigned seat but lets the element slide away from it. Other elements still behave as though the box remained in its original location. ```css .notice { position: relative; top: 0.5rem; left: 1rem; } ``` A containing block is the box that supplies the coordinates the child measures its offsets from. The notice moves down by `0.5rem` and right by `1rem`. Its original space remains reserved, which can leave a visible gap. A relatively positioned box also becomes a containing block for an absolutely positioned descendant. ## Position Absolute Uses a Containing Block `absolute` makes the box leave its assigned seat. Its containing block usually comes from the padding box of the nearest ancestor whose `position` is not `static`. ```css .card { position: relative; } .card-badge { position: absolute; top: 0.75rem; right: 0.75rem; } ``` The card becomes the marked row that supplies coordinates, and the badge sits `0.75rem` from its top-right corner. The card content does not reserve room for the badge, so padding may be needed to prevent an overlap. ## Position Fixed Follows the Viewport CSS `position: fixed` removes the box from normal flow and normally positions it relative to the viewport. An ancestor with a transform, filter, or certain containment settings can establish a different containing block, so check ancestors when a fixed control scrolls unexpectedly. ```css .back-to-top { position: fixed; right: 1rem; bottom: 1rem; } ``` The button stays one rem from the viewport's right and bottom edges. Check fixed controls at narrow widths and high zoom because they can cover links, form fields, or text underneath. ## Position Sticky Stops at a Threshold `sticky` keeps its assigned seat until scrolling reaches a specified inset. The element then stays at that threshold within its nearest scrolling ancestor and containing block. ```css .section-heading { position: sticky; top: 0; background: #eef3f0; } ``` The heading scrolls normally until it touches the top threshold, then remains visible while its containing section continues past it. The background prevents scrolling content from showing through the sticky box. ## Build a Positioned Card Step by Step This sequence changes one layout rule at a time. Begin with normal flow, remove the badge from its assigned seat, define its local coordinate system, then add a sticky heading that keeps its original space. ### Start with Static Flow ```html

New arrivals

New

Desk lamp

Warm light with three brightness levels.

``` With no positioning rules, the heading, badge, title, and description appear in source order. Every element has a place in flow, so nothing overlaps. ### Take the Badge Out of Flow ```css .card-badge { position: absolute; top: 0.75rem; right: 0.75rem; } ``` The badge leaves the document flow, but the card has not supplied a positioning context yet. The browser searches farther up the ancestor chain for a containing block, so the badge may appear near the page corner. ### Add a Positioned Ancestor ```css .product-card { position: relative; padding: 1.25rem; padding-top: 3rem; } ``` The card now supplies the badge's coordinates, so the same offsets pin it to the card's top-right corner. Extra top padding reserves visual room even though the badge itself occupies no flow space. ### Make the Section Heading Sticky ```css .section-heading { position: sticky; top: 0; z-index: 1; background: #eef3f0; } ``` The heading keeps its flow space and scrolls until it reaches the viewport's top edge. It then stays visible within the section. The small `z-index` keeps card content below the opaque heading in the same stacking context, the group of layers a browser composites together. ## Common Pitfalls & Debugging ### Absolute Element Uses the Wrong Ancestor The absolute element appears at a page edge instead of inside its card. No intended ancestor establishes its containing block. Add `position: relative` to the specific parent that should supply the coordinates. ### Z-index Does Not Change the Layer Raising the element's `z-index` value produces no visible change. The element is an ordinary static box, or it sits inside a lower stacking context. Position the element and inspect ancestor stacking contexts. Flex and grid items are the main exception because their `z-index` can work without a non-static position. ### Sticky Does Not Stick The sticky box scrolls away or sticks inside a much smaller area. An ancestor with `overflow: hidden`, `auto`, or `scroll` became the nearest scrolling ancestor. Remove the unnecessary overflow rule or place the sticky element inside the intended scroll container. ### Top or Left Does Nothing An inset declaration has no visible effect. The element still uses `position: static` as part of normal flow. Choose `relative`, `absolute`, `fixed`, or `sticky` according to the layout behavior you need. ## Positioning Best Practices - Keep document structure in normal flow, flexbox, or grid so content can grow without unexpected overlaps. - Give an absolute element a deliberate containing block and reserve visual space when the overlay could cover text. - Use the smallest practical `z-index` within a known stacking context instead of escalating to arbitrary large numbers. - Test fixed and sticky elements with zoom, long content, narrow screens, and keyboard focus before shipping them. ## Frequently Asked Questions ### Does CSS sticky positioning need an explicitly scrolling ancestor? CSS sticky positioning does not need a specially declared scrolling ancestor. It uses the nearest ancestor with a scrolling mechanism, which may be the page. An ancestor with overflow: hidden can become that reference even without visible scrollbars. Set a non-auto inset such as top: 0 and leave enough space for the element to move. ### Can flexbox or grid replace CSS positioning? Flexbox and grid should handle most structural layout because they keep items coordinated with surrounding content. Positioning remains useful for overlays, corner badges, fixed controls, and sticky headings. These systems often work together inside the same component. ### Should CSS positioning use physical or logical inset properties? Use physical CSS insets such as top and right when the physical edge matters. Use logical insets such as inset-block-start and inset-inline-end when placement should follow the writing mode and text direction. ### Does z-index affect where an absolute element is positioned? CSS z-index controls stacking order, not an absolutely positioned element’s coordinates. Its containing block is commonly the nearest positioned ancestor, but properties such as transform can also establish one. Changing z-index alone will not move the element. ## MDN Resources - [The CSS `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/position) - [CSS positioning tutorial](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Positioning) - [Layout and the containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Display/Containing_block) - [Understanding `z-index`](https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Positioned_layout/Understanding_z-index) --- ## CSS Combinators: Descendant, Child, and Sibling Selectors URL: /programming/css/selectors/combinators/ Cluster: Programming > Css A combinator is the symbol between two selectors that tells the browser how the elements should be related before the rule applies. For the broader styling basics, start with the [CSS learning guide](/programming/css/). This guide is part of our [CSS Selectors](/programming/css/selectors/) series. ## Syntax ```css .parent .descendant { /* space: any descendant, nested at any depth */ } .parent > .child { /* >: direct child only */ } .item + .next { /* +: the very next sibling */ } .item ~ .later { /* ~: any following sibling */ } ``` ## The Descendant Combinator A plain space between two selectors matches the second element when it sits anywhere inside the first, no matter how deeply it is nested. This is the combinator you will use most, because it lets you scope styles to a region without touching the same elements elsewhere: ```css .article a { text-decoration: underline; } ``` That rule underlines every link inside `.article`, whether the link sits directly in a paragraph or several levels down in a list. The reach is wide, so keep the left side specific enough that you do not accidentally style links in some unrelated corner of the page. Because the descendant combinator does not care about depth, it also does not care about intervening elements. `.article a` matches a link tucked inside three nested divs just as readily as one sitting directly in the article, which is convenient until a component you did not expect gets nested inside `.article` and quietly inherits styles meant for something else. ## The Child Combinator The `>` symbol tightens things up by matching only direct children of the first element. Nesting no longer leaks your styles downward, which matters a lot for menus and nested lists: ```css .menu > li { border-bottom: 1px solid var(--color-border); } ``` Here only the top-level list items get a border. If a `.menu` item contains its own sub-list, those inner items stay untouched, because they are grandchildren rather than direct children. The child combinator only matches direct children. In `.menu > li`, a list item deeper inside the outer menu does not match through that outer menu. But if the nested list also has the `menu` class, its own direct children match too. Use a different class when the dropdown needs different styles. ## The Sibling Combinators The `+` combinator matches the element immediately after another at the same level, and the `~` combinator matches every following sibling. Both are handy for spacing and for reacting to a neighbor's state: ```css h2 + p { margin-top: 0; } .error ~ .field { opacity: 0.6; } ``` The first rule removes the top margin from a paragraph that directly follows an `h2`, tightening the gap under a heading. The second dims every `.field` that comes after a `.error` element in the same parent. Both sibling combinators only look forward and only within the same parent, so `.item + .next` cannot reach across into a different list even if the markup looks similar. The `+` version is the one you will reach for most, since spacing adjustments almost always care about exactly one neighbor rather than every element that follows. ## Can CSS Select a Parent or Previous Sibling? CSS can select a parent or an earlier sibling through `:has()`. For example, `.card:has(img)` matches a card containing an image, and `h2:has(+ p)` matches a heading immediately followed by a paragraph. Ordinary combinators still describe descendants or following siblings; `:has()` lets you test those relationships from the element you want to style. ## Example ```css .recipe > h3 { margin-block: 1rem 0.25rem; } .recipe h3 + ul { margin-top: 0; } ``` ```html

Ingredients

  • Flour
  • Water
``` The `.recipe > h3` rule spaces the heading because it is a direct child of `.recipe`. The `h3 + ul` rule pulls the list up tight against that heading because the list is the immediate next sibling. Together they give you controlled spacing without adding a single utility class. ## Frequently Asked Questions ### Can CSS child and descendant combinators appear in one selector? CSS child and descendant combinators can appear in the same selector. For example, .card > div a selects links at any depth inside a div that is a direct child of .card. Each combinator describes one relationship. ### Is there a parent selector in CSS? CSS :has() can select a parent based on a matching child or descendant. For example, .card:has(img) selects cards containing an image. It also accepts relative sibling selectors, so it is more flexible than a parent-only selector. ### How many CSS combinators should you chain in one selector? Chain only the CSS combinators needed to identify the intended element. Deep selectors become dependent on the current HTML structure and can stop matching when a wrapper changes. A class on the target element is often clearer. ### Do CSS combinators work with pseudo-classes? CSS combinators work with pseudo-classes. For example, input:checked + label selects the label immediately after a checked input with the same parent. The combinator describes the relationship, while the pseudo-class checks the state. ### Do sibling combinators require both elements to share a parent? CSS sibling combinators require both elements to have the same parent. The + combinator matches the next element sibling, while ~ can match later element siblings. Visual proximity alone does not make elements siblings. --- ## CSS Selectors Guide: Types, Combinators, and Specificity URL: /programming/css/selectors/ Cluster: Programming > Css A CSS selector is the part of a rule that decides which elements on the page get styled. Get the selector right and the rest of CSS calms down a lot. Most of the styling bugs you will chase early on come from a selector matching too much, too little, or losing a fight with another rule you forgot was there. This guide is part of our [CSS guide](/programming/css/) series. ## How Do CSS Selectors Target Elements? CSS selectors match elements by tag name, class, ID, or other characteristics. The browser applies a style rule’s declarations to the elements its selector matches: ```css p { line-height: 1.5; } .card { padding: 1rem; } #site-header { position: sticky; top: 0; } ``` The class selector is the one you will use most of the time, because a class can sit on as many elements as you want. Keep your selectors readable and you will usually be able to predict what they hit before you even reload the page. A type selector like `p` reaches every paragraph in the document, which makes it right for a document-wide default and wrong the moment one paragraph needs to look different. An id selector sits at the other extreme, matching only the one element that carries it, since an id is supposed to be unique on the page. Classes live in between, giving you a repeatable hook for however many elements share that look. ## How Do CSS Combinators Connect Selectors? CSS combinators connect selectors by the relationship between elements. A space between two selectors means "any descendant," and the `>` symbol means "direct child only": ```css .menu a { color: var(--color-link); } .menu > li { margin-block: 0.25rem; } ``` That first rule reaches every link inside `.menu`, however deeply nested. The second rule only touches list items that are immediate children of `.menu`, which is exactly what you want when nesting could otherwise leak your styles somewhere surprising. Combinators add no specificity of their own, only the selectors on either side of the symbol count toward the score. So `.menu > li` and `.menu li` carry identical weight even though the child combinator is far more precise about what it hits. Pick the tighter combinator for correctness, since it wins no specificity fight. ## When Does CSS Specificity Decide Which Rule Wins? CSS specificity decides between matching declarations only after origin, importance, and cascade layer have been resolved. Within that comparison, IDs outrank classes and classes outrank type selectors. A more specific selector cannot override a declaration that already won at an earlier cascade stage. ```css #promo .button { background: rebeccapurple; } .button { background: gray; } ``` The button inside `#promo` ends up purple even though the gray rule comes later in the file, because the id pushes the first selector's score higher. When you understand how that number gets built, you stop reaching for `!important` to force a result. That score is three tiers compared in order: ids first, then classes and pseudo-classes, then tags. A selector only needs to win the leftmost tier to beat a rival, no matter how many classes the losing selector piles on afterward. ## Why Should You Avoid Overcomplicated Selectors? Overcomplicated CSS selectors tie your styles to the current HTML structure and make overrides harder. Before adding another class or !important, inspect the winning rule in developer tools. Check whether your selector matches at all, then fix the actual conflict. ## Topics | Topic | What it covers | Guide | | Selector basics | Type, class, and id selectors and when to use each | [Selector Basics](/programming/css/selectors/selector-basics/) | | Combinators | Descendant, child, and sibling relationships | [Combinators](/programming/css/selectors/combinators/) | | Pseudo-classes | State and position selectors like `:hover` and `:nth-child()` | [Pseudo-classes](/programming/css/selectors/pseudo-classes/) | | Specificity | How the cascade scores selectors and resolves conflicts | [Specificity](/programming/css/selectors/specificity/) | ## Frequently Asked Questions ### Are CSS selectors case-sensitive? CSS type selectors such as div are case-insensitive in HTML. Class and ID selectors are case-sensitive in standards-mode HTML, so .myBox and .mybox match different class names. XML documents have different case rules. ### Do complex selectors slow a page down? Complex CSS selectors can add work during style calculations, especially on large, frequently changing pages. Start by making selectors readable and predictable, then profile the page before treating selector complexity as a performance problem. ### Can a CSS selector match an element based on its text? CSS cannot select an element by matching an arbitrary word or substring in its text. Add a class or data attribute for that purpose, or use JavaScript. Selectors such as :empty can detect some structural conditions but cannot search text. ### What is the difference between a CSS selector and a rule? A CSS selector identifies the elements to style. A CSS style rule contains that selector and the declarations inside the braces. A comma-separated selector list lets one rule target several groups of elements. ### Can CSS select an element using multiple attributes? CSS attribute selectors can be chained so every condition must match the same element. For example, input[type="email"][required] selects required email inputs. Attribute selectors also support prefix, suffix, and substring matching. --- ## CSS Pseudo-classes: Hover, Focus, nth-child, and States URL: /programming/css/selectors/pseudo-classes/ Cluster: Programming > Css A pseudo-class is a keyword starting with a colon that matches an element in a particular state or position, without you having to add any class to the markup yourself. For the broader styling basics, start with the [CSS fundamentals](/programming/css/). This guide is part of our [CSS Selectors](/programming/css/selectors/) series. ## Syntax ```css selector:pseudo-class { /* declarations */ } a:hover { } input:focus { } li:nth-child(2) { } ``` ## State Pseudo-classes The state pseudo-classes match an element while the user is interacting with it, which is how you give a page life. The big ones are `:hover` for the pointer being over an element, `:focus` for an element that has focus from a pointer, keyboard, or script, and `:active` for the moment of a click or tap: ```css .button:hover { background: var(--color-accent-dark); } .button:focus-visible { outline: 3px solid var(--color-accent); outline-offset: 2px; } ``` Use `:focus-visible` when the browser should decide whether to show a focus ring. It usually matches keyboard focus, but can also match a clicked text field. Keep a visible indicator wherever someone needs to see which control has focus. These states matter because a browser tracks them automatically. You never toggle a class in JavaScript to say an element is hovered, the engine already knows the pointer is over it and applies the rule the instant that happens. That automatic tracking is also why `:active` fires so briefly. It only matches during the moment a mouse button or touch is actually down, then reverts the second the press ends. ## Structural Pseudo-classes The structural pseudo-classes match an element by its position among its siblings, so you can style rows, columns, and lists without tagging each item. The workhorse is `:nth-child()`, which takes a number, a keyword, or a small formula: ```css tr:nth-child(even) { background: #f6f8fa; } li:first-child { margin-top: 0; } li:nth-child(3n) { font-weight: 600; } ``` The `even` keyword striped every other table row, `:first-child` cleared the top margin off the opening list item, and `3n` matched every third item. That last formula counts in steps, so `3n` hits the 3rd, 6th, 9th, and so on as the list grows. These selectors save you from adding a `data-index` attribute or a JavaScript loop just to mark every third row. The browser already knows where an element sits among its siblings, so a formula like `3n` or a keyword like `even` reads that position directly. `:first-child` and `:last-child` are shorthand for the edge cases of that same counting, matching only when an element is first or last among its siblings rather than at some arbitrary index. ## Form-state Pseudo-classes Forms carry a third family, and it is the one that saves the most JavaScript. The browser already tracks whether a control is checked, disabled, required, or currently failing its own validation rules, and each of those states has a selector: ```css input:disabled { opacity: 0.5; cursor: not-allowed; } input:required { border-left: 3px solid #d97706; } input:checked + label { font-weight: 600; } ``` The third rule is worth pausing on. `:checked` matches the input, and the sibling combinator moves the styling onto the label next to it, which is how a custom checkbox or radio is usually built without a line of script. Validation states come with a trap. `:invalid` matches as soon as the page loads, so an empty required field is styled as an error before the reader has typed anything, and a form full of red boxes on arrival is the usual result of reaching for it first: ```css /* fires immediately on an empty required field */ input:invalid { border-color: #dc2626; } /* waits until the reader has actually interacted */ input:user-invalid { border-color: #dc2626; } ``` `:user-invalid` exists for exactly that, holding back until the field has been edited or the form submitted. For text fields that display a placeholder, combining `:invalid` with `:not(:placeholder-shown)` can hide the error while the placeholder is visible. This workaround requires a placeholder and does not track interaction; controls without a displayed placeholder can match immediately. ## Selecting a Parent by Its Contents For most of CSS history a selector could only look downward or sideways, never up. `:has()` removes that limit by matching an element based on what it contains. ```css /* a card that contains an image gets different padding */ .card:has(img) { padding-top: 0; } /* a form row whose input is failing validation */ .field:has(input:user-invalid) { border-left: 3px solid #dc2626; } ``` The second example is the one that changes how forms get built. Styling the wrapper around an invalid field used to need a script watching the input and toggling a class on its parent. Now the relationship lives in the stylesheet and the browser keeps it in sync. Two things before relying on it. Its specificity comes from its most specific argument rather than being free, in the same way as `:is()` and `:not()`, which the [specificity guide](/programming/css/selectors/specificity/) works through. And it arrived considerably later than the rest of this page, so confirm it against your own support target rather than assuming it. ## When Should You Use CSS Pseudo-classes? Pseudo-classes are the right call whenever a style depends on something the browser already tracks, like hover, focus, or position among siblings. Reach for a real class instead the moment the condition is something only your application logic knows, like whether an item is the user's favorite or whether a form field failed validation server-side. Mixing the two approaches within a single stylesheet is completely normal. A shopping cart might use `:hover` for a delete button and a `.is-selected` class for an item the user picked, side by side in the same stylesheet. ## Why Does :nth-child() Match the Wrong Element? `:nth-child()` looks at an element's position among all of its siblings before it checks whether your selector matches. So `p:nth-child(2)` means "an element that is the second child and also happens to be a paragraph," which fails the moment a heading sits in slot two. This trips people up constantly because `:nth-child()` reads exactly like it should mean "the nth paragraph," and the name gives no hint that siblings of other tags are silently included in the count. When you really want the second paragraph regardless of what else is mixed in, use `:nth-of-type(2)` instead, since that counts only the elements of the same type: ```css /* second child overall, only if it is a

*/ p:nth-child(2) { } /* the second

, whatever sits between them */ p:nth-of-type(2) { } ``` ## Example ```css .nav a:hover, .nav a:focus-visible { text-decoration: underline; } .price-list li:nth-child(odd) { background: #fafafa; } ``` ```html

  • Starter
  • Pro
  • Team
``` The nav links underline on both hover and keyboard focus, so pointer users and keyboard users get the same cue. In the price list, the 1st and 3rd items pick up the light background from `:nth-child(odd)`, giving you zebra striping without a single extra class. The browser recalculates those positions automatically if you add or remove an item later. ## Frequently Asked Questions ### What is the difference between a pseudo-class and a pseudo-element? A CSS pseudo-class selects an element by a condition, such as :hover or :first-child. A pseudo-element styles a particular part, such as ::first-line, or generates a box, such as ::before. Pseudo-elements normally use two colons. ### Why does :hover not work on touch devices? CSS :hover is unreliable on touch-only devices because there is no pointer resting over the element. Some browsers apply hover after a tap. Keep essential controls available through taps, keyboard focus, and ordinary visible content. ### What is the difference between :focus and :focus-visible? CSS :focus matches any focused element. :focus-visible matches a focused element when the browser decides a visible indicator is needed, often during keyboard navigation. It can also match a clicked text field, so it does not mean keyboard-only focus. ### Can CSS pseudo-classes be chained together? CSS pseudo-classes can be chained so their conditions must all match. For example, a:hover:not(.disabled) requires a hovered link without that class. Most pseudo-classes add class-level specificity; :where() adds none, while :is(), :not(), and :has() use their most specific argument. ### Can CSS :nth-child() use formulas or count from the end? CSS :nth-child() accepts An+B formulas, such as 2n for even children and -n+3 for the first three children. Use :nth-last-child() to count from the end. The keywords odd and even cover the common alternating patterns. --- ## CSS Selector Basics: Type, Class, ID, and When to Use Each URL: /programming/css/selectors/selector-basics/ Cluster: Programming > Css The three selectors you will use constantly are the type selector, the class selector, and the id selector, and each one targets elements a different way. For the broader styling basics, start with the [guide to CSS](/programming/css/). This guide is part of our [CSS Selectors](/programming/css/selectors/) series. ## Syntax ```css p { /* type: every

element */ } .card { /* class: every element with class="card" */ } #header { /* id: the one element with id="header" */ } ``` ## Type Selectors A type selector matches every element of a given tag name, so `p` styles all your paragraphs at once. These are great for setting sensible defaults across a whole document, like a base line-height or a heading color: ```css h2 { margin-block: 1.5rem 0.5rem; } a { color: var(--color-link); } ``` Because a type selector hits every matching tag, it reaches wide by design. Use it for the baseline look of an element, then narrow things down with a class wherever a specific instance needs to differ. A type selector has lower specificity than a class or ID selector. When importance and cascade layer are equal, the more specific selector wins. That makes type selectors useful for defaults that a class can override later. ## Class Selectors A class selector starts with a dot and matches any element carrying that class in its `class` attribute. This is the selector you will reach for most, since one class can apply to many elements and one element can carry several classes: ```css .button { padding: 0.5rem 1rem; border-radius: 0.375rem; } .button.is-primary { background: var(--color-accent); } ``` That second rule only matches elements with both `button` and `is-primary` on them. Classes give you reusable, predictable styling hooks, which is why component-driven CSS leans on them so heavily. You can also stack classes on one element the way `.button.is-primary` does above, layering a specific variant on top of a general pattern instead of writing a whole new rule. That is the idea behind naming systems like BEM, where a block class supplies the shared look and a modifier class swaps out one piece of it. Reusing classes this way keeps a stylesheet shorter and keeps your intent readable when you come back to it months later. ## ID Selectors An id selector starts with a hash and targets the single element whose `id` matches. An id is supposed to be unique on the page, so reach for one when you mean one specific element: ```css #main-nav { position: sticky; top: 0; } ``` An id is not only a styling hook. It anchors in-page links, so a URL ending in `#main-nav` jumps the browser straight to that element, and it gives JavaScript a fast way to grab one specific node with `document.getElementById`. That dual duty is exactly why you want styling-by-id to stay light. The moment a script or an anchor link depends on that id, a styling change means touching code elsewhere too. ## Should You Use a Type, Class, or ID Selector? Reach for a type selector when a style should apply everywhere that tag shows up. Reach for a class when you need a repeatable pattern that some elements opt into and others skip. Save an id for the rare element that is one of a kind, like a page header or a single modal root. Most real stylesheets lean hard on classes for exactly this reason, using type selectors sparingly for real document-wide defaults and ids sparingly for landmarks. Writing an id selector just to win a specificity fight is usually a sign to add a class instead. ## Why Are ID Selectors Hard to Override? An ID selector has higher specificity than a class selector. When importance and cascade layer are equal, the ID rule wins, which can make it awkward to override later. You set a `#main-nav` color, then a `.dark-theme` class fails to change it, and the page looks broken for no obvious reason. Lean on classes for styling and save ids for anchors and JavaScript hooks, and your overrides will keep working. The [specificity guide](/programming/css/selectors/specificity/) walks through exactly how that scoring adds up. ## Example ```css /* type: all paragraphs get comfortable spacing */ p { margin-block: 0 1rem; } /* class: reusable highlight you can drop anywhere */ .highlight { background: #fff3cd; } /* id: one specific landmark element */ #page-footer { border-top: 1px solid var(--color-border); } ``` ```html

This paragraph gets both the type rule and the class rule.

This footer gets the id rule.
``` The paragraph picks up its spacing from the type rule and its yellow background from the class, since both selectors match it. The footer takes its top border from the id rule. Each element ends up wearing every rule whose selector it matched. ## Frequently Asked Questions ### Can an element have more than one class? An HTML element can have multiple space-separated class names. CSS rules for each matching class can apply to the element, with the cascade resolving conflicting declarations. ### Is it valid to reuse the same id on more than one element? An HTML id must be unique within its document. CSS may still style duplicate IDs, but duplicate values make fragment links and JavaScript lookups unreliable. Use a class when several elements need the same styling. ### Can a class name start with a number? An HTML class name can start with a number, but a CSS class selector must escape that leading digit. Starting class names with a letter avoids that extra syntax. A hyphen followed by a digit also needs escaping in a CSS identifier. ### What does the CSS universal selector do? The CSS universal selector, *, matches every element within its scope and adds zero specificity. It is useful for shared defaults such as box-sizing. Other cascade rules still decide whether its declarations win. ### Do inline styles count as a selector? Inline styles have no selector. Normal declarations in an HTML style attribute take precedence over normal author stylesheet declarations. An important author declaration can override a normal inline declaration, but an important inline declaration has stronger precedence. --- ## CSS Specificity: How the Cascade Picks the Winning Rule URL: /programming/css/selectors/specificity/ Cluster: Programming > Css Specificity is the score the browser gives each selector to decide which rule wins when several of them set the same property on the same element. This guide is part of our [CSS Selectors](/programming/css/selectors/) series. ## Syntax ```css /* the browser reads each selector as three tiers: */ /* (ids, classes/attributes/pseudo-classes, types/pseudo-elements) */ #main /* (1, 0, 0) */ .card .title /* (0, 2, 0) */ a:hover /* (0, 1, 1) */ ul li /* (0, 0, 2) */ ``` ## How Is CSS Specificity Calculated? CSS specificity counts a selector across three columns and compares them from left to right. An id is worth one point in the first bucket, a class or attribute or pseudo-class is worth one in the second, and a tag or pseudo-element is worth one in the third: ```css #promo { /* (1, 0, 0) */ color: red; } .banner .text { /* (0, 2, 0) */ color: blue; } ``` A single id beats any number of classes, because the first bucket is compared before the second one even matters. So `#promo` wins the color here despite `.banner .text` carrying two classes. The comparison never adds the buckets together into one number, it walks them in order until one selector pulls ahead. Inline styles and the universal selector sit outside this same three-tier count. An inline `style` attribute on an element beats every normal declaration in your stylesheet regardless of score, though a rule marked `!important` can still override it, so inline values can make theme overrides harder. The universal selector, `*`, sits at the opposite extreme and scores `(0, 0, 0)`, so it loses to nearly everything. ## When Does Source Order Break a CSS Tie? Source order breaks a CSS tie when origin, importance, layer, specificity, and scoping proximity are equal. The later valid declaration wins for that property. In this unlayered example, both selectors have the same weight, so the second background applies. ```css .button { background: gray; } .button { background: green; } ``` Both selectors score `(0, 1, 0)`, so the second one wins and the button comes out green. Load order across files counts too, which is how a stylesheet linked later can quietly override an earlier one without any change in specificity. Source order applies across every stylesheet the page loads, spanning all of them as a single sequence. If your build tool concatenates or bundles files in a different order than you expect, two selectors that tie on specificity can swap which one wins without a single line of CSS changing. That is a common cause of a rule that works locally but flips in production. ## How Does !important Affect the CSS Cascade? CSS `!important` gives a declaration precedence over normal declarations from the same author origin, before specificity is compared. Between important declarations, origin and layer order still matter. A larger specificity score cannot overcome those earlier decisions. ```css .alert { color: black !important; } #urgent .alert { color: red; /* loses, even with an id, because the other rule is !important */ } ``` The `#urgent .alert` rule has a higher raw score, yet it still loses because the other declaration is marked important. When you find yourself stacking `!important` to win, slow down and fix the selector or the load order instead. The [selector basics guide](/programming/css/selectors/selector-basics/) covers why leaning on ids creates this kind of trap in the first place. Among important declarations in the same origin and layer, specificity and then the remaining cascade tie-breaks decide the winner. Important layer order is reversed, as the layer example below explains. Inspect the cascade before increasing a selector’s weight. ## How Does :where() Keep Specificity Low? CSS `:where()` adds zero specificity, including the selectors inside its parentheses. That makes it useful for defaults you want other rules to override. Selectors outside the parentheses still contribute their normal weight. ```css :where(#sidebar, .widget) a { /* (0, 0, 1) — only the a counts */ color: navy; } #sidebar a { /* (1, 0, 1) */ color: teal; } ``` The `:where(#sidebar, .widget)` part contributes zero specificity, but the trailing `a` still contributes one type selector. A class selector can override this normal rule within the same cascade layer. That keeps defaults easier to change. Its sibling `:is()` behaves differently and the difference catches people out. `:is()` takes the specificity of the most specific selector inside it, so `:is(#sidebar, .widget)` scores as an id even when it matched on the class. The same rule applies to `:not()` and `:has()`. Reach for `:is()` when you want the grouping to be concise, and `:where()` when you want the grouping to be weak. ## Do Cascade Layers Override Specificity? CSS cascade layers take precedence over selector specificity within the same origin and importance level. For ordinary declarations, a rule in a later layer beats a rule in an earlier one whatever their scores: ```css @layer base, theme; @layer base { #sidebar .widget a { color: navy; } /* (1, 1, 1) */ } @layer theme { a { color: teal; } /* (0, 0, 1) — wins */ } ``` The plain `a` selector wins because `theme` is declared after `base`, and the layer comparison happens before either score is looked at. Declarations outside any layer are stronger still, which is worth knowing before you wrap an existing stylesheet in a layer and watch its precedence change. All of that describes normal declarations, and `!important` turns it upside down. Among important declarations the layer order reverses, so an important rule in an earlier layer wins, and important rules inside a layer outrank important rules outside every layer. The practical reading is that layers give you a deliberate ordering for your normal styles and a mirror image of it for your emergencies, which is another reason not to reach for `!important` to settle a layered stylesheet. The [CSS guide](/programming/css/) sets out the full order the cascade works through. Between them, these two features replace most of what `!important` used to be needed for. When a rule refuses to apply, the useful question is no longer "how do I score higher" but "which stage is this being decided at", because adding weight to a selector cannot win an argument that was settled a stage earlier. ## Example ```css #sidebar .widget a { /* (1, 1, 1) */ color: navy; } .widget a { /* (0, 1, 1) */ color: teal; } ``` ```html
Home
``` The link comes out navy because the first selector carries an id and scores `(1, 1, 1)`, which beats the id-free `(0, 1, 1)` of the second rule. Drop the `#sidebar` from that first selector and both rules tie, at which point source order takes over and the later one wins. Once you can read those three numbers off a selector, the cascade stops feeling random. ## Frequently Asked Questions ### Does the order of class names in the HTML attribute change which rule wins? The order of class names in an HTML class attribute does not affect CSS precedence. The cascade resolves conflicts using origin, importance, layers, specificity, scoping proximity, and source order rather than the order of those class names. ### Do CSS cascade layers change how specificity works? CSS cascade layers are compared before specificity. For normal author declarations, later layers beat earlier layers and unlayered styles beat layered styles. Important declarations reverse that layer order, so specificity alone cannot explain which rule wins. ### Does the :where() pseudo-class affect specificity? CSS :where() and its arguments always contribute zero specificity. Selectors outside :where() still count. By comparison, :is() contributes the specificity of its most specific argument. ### How do you override a CSS style you cannot edit? To override a CSS style, first inspect why it wins in browser developer tools. For normal rules in the same origin and layer, use a matching selector with greater specificity, or equal specificity later in the stylesheet when scope also ties. A later layer helps only against earlier layered normal rules; it cannot beat unlayered normal styles. ### Is specificity calculated per rule or per declaration? CSS specificity is calculated for a selector, while the cascade chooses a winning declaration separately for each property. An element can therefore get its color from one rule and its padding from another. --- ## CSS Colors and Units: Hex, RGB, Rem, Em, and Viewport Units URL: /programming/css/typography-and-color/colors-and-units/ Cluster: Programming > Css CSS color formats are different recipes for mixing the same paint, while each CSS unit tells the browser which reference to measure from. Start with the syntax below, then choose a format or unit whose reference is clear to the next person reading the rule. For the broader styling basics, start with the [guide to CSS](/programming/css/). This guide is part of our [CSS typography and color guide](/programming/css/typography-and-color/) series. ```css .notice { color: hsl(222 47% 11%); background: rgb(219 234 254 / 80%); border: 1px solid #93c5fd; padding: 1rem; width: 80%; } ``` The notice uses HSL for opaque text, RGB with an alpha channel for a translucent background, hex for a compact border color, `rem` for root-relative spacing, and a percentage width tied to its parent. ## CSS Color and Unit Syntax A color value can be a keyword, a hexadecimal string, or a function. Modern `rgb()` and `hsl()` syntax separates channels with spaces. If the color needs transparency, a slash separates the three color channels from the optional alpha value. Lengths combine a number with a unit, such as `16px`, `1rem`, or `40vw`. A percentage is resolved against a property-specific reference. For example, `width: 50%` uses the containing block's width, while `font-size: 120%` uses the parent's font size. ## Color Keywords Named colors make early examples easy to read because their meaning is visible in the declaration. CSS defines keywords such as `tomato`, `rebeccapurple`, and `transparent`, but a product palette usually needs more precise values than names provide. ```css .status { color: navy; background: lightyellow; border-color: tomato; } ``` Use keywords for learning, quick prototypes, and familiar values such as `transparent`. Use a numeric format when a design requires an exact shade. ## Hex Colors Hex writes the paint recipe as compact red, green, and blue hexadecimal pairs after `#`. The six-digit value `#2563eb` copies cleanly from design tools. Three-digit shorthand works only when every pair repeats, so `#fff` expands to `#ffffff`. ```css .button { color: #ffffff; background: #2563eb; border-color: #1d4ed8; } ``` An eight-digit hex value adds an alpha pair at the end. The eight-digit example `#2563eb80` uses approximately 50% opacity. The compact form is valid, but the alpha pair is less obvious than slash-alpha syntax during review. ## RGB Colors and Slash Alpha `rgb()` spells out the same paint recipe through red, green, and blue channels written as numbers from 0 to 255 or percentages from 0% to 100%. Modern syntax uses spaces between channels, then an optional slash followed by a number or percentage for alpha. ```css .overlay { background: rgb(15 23 42 / 70%); } .overlay-light { background: rgb(15 23 42 / 0.25); } ``` The first background is 70% opaque, and the second is 25% opaque. This syntax is a good fit when a color comes from RGB channel values or when transparency needs to remain immediately visible. ## HSL Colors and Slash Alpha The `hsl()` paint recipe uses hue, saturation, and lightness. Hue moves around a color wheel, saturation controls color intensity, and lightness moves from black through the chosen color to white. Keeping hue and saturation steady makes related light and dark variants easier to edit by hand. ```css .tag { background: hsl(217 91% 60%); } .tag-soft { background: hsl(217 91% 85% / 80%); } ``` The slash before `80%` performs the same alpha job as it does in `rgb()`. Both functions use the modern space-separated form, so the color channels stay visually separate from opacity. ## Which CSS Color Format Should You Use? Use hex for compact color codes, rgb() for red/green/blue channels, and hsl() when adjusting hue, saturation, and lightness is easier. Each can describe the same sRGB color; consistency within your project matters more than choosing one universally best format. | Format | Best fit | | Keyword | Readable examples, prototypes, and familiar special values. | | Hex | Compact opaque colors copied from a design specification. | | `rgb()` | RGB channel values and clearly written transparency. | | `hsl()` | Related shades adjusted through hue, saturation, or lightness. | ## How Do CSS Length Units Work? CSS length units tell the browser what a measurement is relative to. Pixels are reference units, rem follows the root font size, em uses the parent’s font size when setting font-size and the element’s own font size for most other lengths, and viewport units follow the browser viewport. Percentages use a reference determined by the property. - `px` is an absolute CSS length suited to thin borders and small fixed details. - `rem` uses the root element's font size, which makes it a steady choice for type and spacing. - `em` on `font-size` uses the parent's font size; on most other properties, it uses the element's own font size. - `%` uses a reference defined by the property, often the parent or containing block. - `vw` equals 1% of the viewport width and suits dimensions that intentionally track the screen. ## Worked Card Example ```css .card { width: 80%; max-width: 40rem; padding: 1.5rem; border: 1px solid #93c5fd; color: hsl(222 47% 11%); background: rgb(219 234 254 / 80%); } .card__title { margin-bottom: 0.5em; font-size: 1.5rem; } ``` With a 16px root size, the card's `1.5rem` padding computes to 24px and the title's `1.5rem` size also computes to 24px. The title's `0.5em` bottom margin then computes from that local 24px font size, producing 12px. The percentage width follows the parent, while `max-width` stops the card from growing indefinitely. ## Common Pitfalls & Debugging ### em Values Compound Through Nesting Nested text gets larger at every level. Each element uses an `em` font size calculated from its parent's already enlarged value. Use `rem` when every level should share one root-based size. ```css .menu { font-size: 1.2em; } /* Fix: rem replaces the compounding em. */ .menu .menu { font-size: 1rem; } ``` ### Hex Alpha Is Easy to Misread An eight-digit hex color has unexpected opacity. Its final pair encodes alpha in hexadecimal, so `80` means about 50%, not 80%. Use slash-alpha RGB or HSL when reviewers need to read transparency directly. ### Mixed Units Hide the Scaling Rule Related spacing changes at different rates. The component mixes root-relative, local-font, parent, and viewport references without a reason. Choose units by job, document exceptions, and keep related values on the same scale. ## Choose Units for What Should Resize Choose a color recipe that makes its channels readable, then choose each unit from the reference the property should follow. Consistent choices make later edits easier to predict. ## Frequently Asked Questions ### Is a CSS pixel one device pixel? A CSS pixel is a reference unit rather than a guaranteed physical screen dot. High-density screens can use several device pixels to render one CSS pixel, and browser zoom can scale dimensions written in px. ### Can a CSS calc() expression mix units? CSS calc() can combine compatible units, such as calc(100% - 2rem) for a width. The browser resolves each part against its appropriate reference. The result must still be a valid value for the property using it. ### What does one rem equal in CSS? One rem equals the root element’s computed font size. That commonly starts at 16px, but browser preferences or your CSS can change it. Avoid fixing the root size in pixels if you want rem-based text to follow the reader’s default preference. ### Should CSS layout widths use percentages or vw? Use percentage widths when an element should scale with its containing block. Use vw when it should scale with the viewport. Add a suitable maximum width so text does not stretch into long, difficult lines. ### Can CSS custom properties store color values? CSS custom properties can store reusable colors. Declare a name such as --brand-color on :root or a component, then use var(--brand-color) in color properties. Changing that value updates the elements that use it, unless a local declaration overrides it. --- ## CSS Custom Properties: Variables, Fallbacks, and Themes URL: /programming/css/typography-and-color/custom-properties/ Cluster: Programming > Css CSS custom properties give reusable values a name. A declaration beginning with two dashes stores the value, and the `var()` function reads it inside another property value. For the broader styling basics, start with the [CSS fundamentals](/programming/css/). This guide is part of our [CSS typography and color guide](/programming/css/typography-and-color/) series. ```css :root { --color-accent: #2563eb; } .button { background: var(--color-accent); } ``` Think of each custom property as a label attached to a CSS value. The label can begin at the document root, then a component or theme can attach a different value to the same label within its own scope. ## What Do CSS Custom Properties Do? CSS custom properties let several declarations reuse a named value and let components or themes override that value. They follow the cascade and normally inherit. Registration with `@property` can change inheritance and restrict the value type. The browser keeps the stored value as a sequence of tokens until `var()` substitutes it into a real declaration. A color label can therefore feed `color`, `background`, or `border-color`, provided that the final substituted value is valid for the receiving property. Document-wide custom-property defaults usually belong on the `:root` element. Component-specific values belong on the component container, where inheritance carries them only through the part of the document that needs the override. ## Define Shared Values at the Document Root The `:root` pseudo-class matches the document's root element, which is `html` in an HTML page. Values declared there are available to descendants through inheritance. ```css :root { --color-text: #1f2937; --color-accent: #2563eb; --space-md: 1rem; --radius-md: 0.5rem; } ``` Names should describe the value's role when that role is stable. `--color-accent` remains useful if the accent changes from blue to orange, while `--blue` becomes misleading after the same edit. ## Read a Custom Property Value Pass the complete custom property name to `var()`. The browser replaces that function with the computed value available on the current element. ```css .card { color: var(--color-text); border: 1px solid var(--color-accent); padding: var(--space-md); border-radius: var(--radius-md); } ``` Changing `--color-accent` at the root updates every declaration that reads the label, unless a nearer rule overrides it. ## Add a Fallback Value The fallback argument in `var()` is used when an unregistered custom property is missing or has an invalid custom-property value, such as a cycle. It does not rescue a value that exists but is wrong for the receiving property, such as `16px` used as a color. ```css .alert { color: var(--color-danger, #b91c1c); padding: var(--alert-space, var(--space-md, 1rem)); } ``` The alert uses `#b91c1c` when `--color-danger` is undefined. Its padding tries a component value, then the shared spacing label, then a final direct length. ## Override a Value in One Scope Redeclare the same label on a container to change descendants inside that subtree. The rest of the document continues to inherit the root value. ```css :root { --color-accent: #2563eb; } .warning-panel { --color-accent: #c2410c; } .panel-link { color: var(--color-accent); } ``` A panel link inside `.warning-panel` becomes orange, while the same link class elsewhere remains blue. The label stays the same because its job remains "accent color" in both scopes. ## Build a Theme Switch A theme can override a small set of labels on the root element. Components keep reading the same names, so their rules do not need separate light and dark versions. ```css :root { --page-bg: #eef3f0; --page-text: #1f2937; --link-color: #1d4ed8; } :root[data-theme="dark"] { --page-bg: #17211d; --page-text: #e5eee9; --link-color: #93c5fd; } body { color: var(--page-text); background: var(--page-bg); } a { color: var(--link-color); } ``` ```html ... ``` Changing the `data-theme` attribute switches the values inherited by the page. JavaScript can update that attribute after a reader selects a theme, while CSS remains responsible for the visual rules. ## How Can JavaScript Read and Change CSS Variables? JavaScript reads a CSS custom property with `getComputedStyle(element).getPropertyValue()` and changes it with `element.style.setProperty()`. Unlike a Sass variable, a custom property remains available in the browser while the page runs. ```js const root = document.documentElement; // read the current value const brand = getComputedStyle(root) .getPropertyValue("--color-brand") .trim(); // set a new one root.style.setProperty("--color-brand", "#7c3aed"); ``` The `.trim()` is defensive rather than decorative. Browsers have differed over whether the returned value keeps the whitespace that followed the colon in the source, so a comparison against a bare string can fail in one engine and pass in another. Trimming costs nothing and removes the inconsistency. Calling `setProperty()` on `documentElement` writes an inline style on the HTML root. Descendants inherit the value unless another declaration or registration changes that behavior. Set it on a component element when only that component should change. For an unregistered custom property, a value can be accepted as data but become invalid when another property uses it. Setting `--accent: 16px` does not produce a useful color. A declaration that tries to use it as a color falls back to the inherited or initial color instead. ## Registering a Typed Property An unregistered CSS custom property does not smoothly interpolate between values. Register its type with `@property` when you want a transition on that custom property, such as an angle moving from `0deg` to `90deg`. `@property` registers a type and fixes that: ```css @property --angle { syntax: ""; inherits: false; initial-value: 0deg; } .dial { --angle: 0deg; transition: --angle 300ms ease; transform: rotate(var(--angle)); } .dial:hover { --angle: 90deg; } ``` `syntax` names the type and `inherits` decides whether the value passes down the tree; both are always required. `initial-value` is required too for any concrete syntax like ``, and is only optional when the syntax is the universal `*`, which is the case that opts back out of typing. With a real type in place the transition has two endpoints to interpolate between and the rotation sweeps smoothly. Registration also validates values against the declared type. In this example, `inherits: false` and `initial-value: 0deg` provide a defined default when no usable value is available. Check browser support before making a typed-property animation essential to using the page. ## Common Pitfalls & Debugging ### A Value Becomes Invalid at Computed-value Time A declaration falls back to its inherited or initial value. The custom property exists, but its substituted value is invalid for the receiving property. Inspect the computed declaration and store a value of the correct type. ```css :root { --text-color: 16px; } .notice { color: blue; color: var(--text-color); } ``` The browser accepts `16px` as custom-property data before it knows where the label will be used. Once substituted into `color`, the value becomes invalid at computed-value time. A `var()` fallback does not help here because the custom property exists. ### Inheritance Changes More Elements than Expected A component override affects deeper descendants. Two-dash custom properties inherit by default. Place the override on the narrowest useful container or redeclare the label where inheritance should stop. ### Custom Properties Do Not Work in Media Query Conditions A media query containing `var()` never matches. The function substitutes values only inside CSS property declarations. Selectors and query conditions cannot use that substitution. Write the breakpoint directly in the condition and use custom properties inside its rules. ```css /* Invalid */ @media (min-width: var(--breakpoint-md)) { .sidebar { display: block; } } /* Valid */ @media (min-width: 48rem) { .sidebar { --sidebar-space: 1.5rem; } } ``` The valid query keeps the breakpoint as a direct length. The custom property remains inside the declaration block, where CSS allows it to store a value for descendant rules. ## Name the Values You Reuse Name values that repeat or change by scope, then let the cascade carry those names to the elements that use them. Keep one-off values directly in their declarations. ## Frequently Asked Questions ### Are CSS custom properties the same as JavaScript variables? CSS custom properties hold values that participate in the cascade and are read with var(). JavaScript variables belong to JavaScript and follow different scope rules. Scripts can still read and update CSS custom properties through browser APIs. ### Do CSS custom properties inherit? Unregistered CSS custom properties inherit by default. Registering a property with @property lets you disable inheritance with inherits: false and set an initial value. A declaration on a child can override an inherited custom property. ### When should a project use CSS custom properties? Use CSS custom properties for values that repeat, vary between components, or change together in a theme. Shared colors and spacing are good candidates. A single measurement can stay directly in its declaration when a name adds no clarity. ### Can CSS custom properties be set in an inline style attribute? CSS custom properties can be declared in an HTML style attribute. The value applies to that element and normally inherits to descendants, unless registration disables inheritance or another declaration overrides it. This works well for a per-item accent color supplied by your application. ### Can CSS calc() use a custom property? CSS calc() can use a custom property through var(), as in calc(var(--space-md) * 2). The substituted value must make the expression valid for its destination property, such as a length for padding. ### Can CSS custom properties affect performance? Changing a CSS custom property can trigger style recalculation for elements that depend on it. Broadly inherited values updated repeatedly deserve profiling on large pages. For ordinary colors and spacing, choose useful names and avoid needless updates rather than assuming variables are slow. --- ## CSS Fonts and Text: Family, Size, Weight, and Line Height URL: /programming/css/typography-and-color/fonts-and-text/ Cluster: Programming > Css CSS font properties choose the typeface, size, weight, and line height used to draw text. Text properties adjust spacing, alignment, decoration, and wrapping after the font has been selected. For the broader styling basics, start with the [CSS learning guide](/programming/css/). This guide is part of our [CSS typography and color guide](/programming/css/typography-and-color/) series. ```css .article { font-family: system-ui, sans-serif; font-size: 1rem; line-height: 1.6; color: #1f2937; } .article h2 { font-size: 1.75rem; font-weight: 700; text-wrap: balance; } ``` Think of a font stack as a queue of typefaces. The browser checks each option in order for the character and style it needs, then uses the generic family at the end when earlier choices are unavailable. ## What Do CSS Font and Text Properties Control? CSS `font-family` selects typefaces, `font-size` sets their scale, and `font-weight` selects thickness. `line-height` controls the height of each line box. Then `letter-spacing`, `word-spacing`, and `text-wrap` adjust how text occupies the available line. Most of these properties inherit, so a body rule can establish readable defaults while headings and controls override only the values that need to differ. That keeps the font queue and its basic measurements consistent across the document. ## Build a Font-family Stack A `font-family` value is a comma-separated priority list. Quote family names that contain spaces, and always finish with a generic family such as `sans-serif`, `serif`, or `monospace`. ```css body { font-family: "Source Sans 3", system-ui, sans-serif; } code { font-family: "IBM Plex Mono", ui-monospace, monospace; } ``` The browser tries the named web or local font first, then the platform's user-interface font, then any available sans-serif font. Font selection also happens character by character, so later entries can supply glyphs missing from the first family. A minimal system-font stack can use `system-ui, sans-serif`. It loads without a font download and suits interface text, but the exact typeface changes with the operating system. ## Set Font Size and Line Height Use `rem` for most font sizes so text follows the root size. A unitless `line-height` multiplies each element's own font size, which lets descendants inherit the proportion instead of a fixed measurement. ```css body { font-size: 1rem; line-height: 1.6; } h1 { font-size: 2.25rem; line-height: 1.15; } ``` With a 16px root size, body text computes to 16px with a 25.6px line box. The heading computes to 36px with a 41.4px line box. The heading uses a tighter multiplier because large type usually needs less vertical space between lines. ## Choose a Font Weight `font-weight` accepts keywords such as `normal` and `bold` or numbers from 1 to 1000. Common static fonts supply selected values such as 400 and 700, while a variable font may support a continuous range. ```css .summary { font-weight: 400; } .summary strong { font-weight: 700; } ``` Request weights that the loaded font actually provides. If the exact value is missing, the browser chooses an available weight and may synthesize a bold face unless font synthesis is disabled. Two or three intentional weights usually create enough hierarchy for a page. ## Adjust Letter and Word Spacing `letter-spacing` changes the space between characters, while `word-spacing` changes the space between words. The initial `normal` value lets the browser and font use their expected spacing. ```css .eyebrow { font-size: 0.875rem; letter-spacing: 0.04em; } .article { letter-spacing: normal; word-spacing: normal; } ``` A small spacing adjustment can suit a short label, but body text should usually keep the font's normal character and word spacing. Large positive or negative values can separate letters from their word shapes or crowd them together. ## Control Text Wrapping The `text-wrap` shorthand controls soft line wrapping. `wrap` is the normal behavior, `nowrap` keeps text on one line and can cause overflow, and `balance` redistributes short blocks across a limited number of lines. The `pretty` value asks the browser to favor improved body-copy wrapping. ```css .card__title { text-wrap: balance; } .card__summary { text-wrap: pretty; } .status-label { text-wrap: nowrap; } ``` Use `balance` for headings rather than long paragraphs. Reserve `nowrap` for compact content that has a clear overflow plan, since an unbroken label can push beyond a narrow container. ## Build a Readable Article Card Begin with inherited body defaults, then change the title and metadata one property at a time. ```css body { font-family: system-ui, sans-serif; font-size: 1rem; line-height: 1.6; } .article-card { max-width: 42rem; } .article-card__title { margin: 0; font-size: 1.75rem; font-weight: 700; line-height: 1.2; text-wrap: balance; } .article-card__meta { font-size: 0.875rem; color: #4b5563; } ``` The card inherits the system-font queue and readable body line height. Its title increases size and weight while tightening line height, and the metadata steps down in size without changing the family. A maximum width prevents the text line from stretching across an unrestricted container. ## Common Pitfalls & Debugging ### A Web Font Briefly Hides Text Text stays invisible while a font downloads. The chosen font-display strategy includes a block period. Choose an appropriate `font-display` value so a fallback can appear while the file loads. ```css @font-face { font-family: "Article Sans"; src: url("/fonts/article-sans.woff2") format("woff2"); font-display: swap; } ``` With `swap`, the browser can show fallback text promptly and replace it when Article Sans becomes available. ### The Fallback Font Changes Line Breaks Text shifts when the web font replaces its fallback. The two fonts have different character widths or vertical metrics. Choose a fallback with similar proportions and test headings, controls, and narrow cards during the swap. ### A Fixed Line Height Does Not Scale Inherited headings look cramped or overlap. A parent supplied a fixed line height such as `24px`. Use a unitless value so each descendant multiplies the line height by its own font size. ### A System-font Stack Varies by Platform Text wraps differently on Windows, macOS, and mobile devices. `system-ui` selects the platform's interface font. Treat platform variation as part of the system-font choice and test the layout with realistic content instead of assuming identical metrics. ## Check the Text at Reading Size Build a dependable font stack, size it with root-relative values, and use unitless line height so text scales with its element. Test fallback metrics anywhere line breaks affect the layout. ## Frequently Asked Questions ### Does CSS font-weight: bold look the same in every typeface? CSS font-weight: bold requests weight 700, but that weight looks different across typefaces. If a font lacks the requested weight, the browser uses its font-matching rules or may synthesize a heavier appearance. ### What is a good CSS line-height for body text? A unitless CSS line-height around 1.5 is a useful starting point for body text. Adjust it for the typeface and line length. Headings can often use tighter spacing because their letters are larger and their lines are shorter. ### Can CSS letter-spacing use units other than em? CSS letter-spacing accepts length units such as px, rem, and em. The em unit scales with the element’s own font size, making it a useful choice when letter spacing should grow with the text. ### Does text-transform change the underlying text? CSS text-transform changes how text is displayed, not the underlying text in the document. Copying and assistive-technology behavior can vary, so do not rely on it to change stored values or control how text is announced. ### Does text-wrap: balance have consistent browser support? CSS text-wrap: balance works in current major browsers, but older browsers may ignore it and use normal wrapping. Treat balanced wrapping as an enhancement for headings and short blocks, and check the browsers you support. --- ## Typography and Color in CSS URL: /programming/css/typography-and-color/ Cluster: Programming > Css CSS typography and color control how text looks, how readable it feels, and how visual emphasis carries across a page. Color values supply the palette, length units set scale, font properties shape the text, and custom properties keep repeated choices under one name. This guide is part of our [CSS guide](/programming/css/) series. Think of these features as a small type and paint kit. You choose a color, measure the text and spacing, select a typeface, then label any value that the rest of the project needs to reuse. Each choice stays small enough to learn on its own. ## What Do CSS Typography and Color Properties Control? CSS typography properties control the typeface, size, spacing, and wrapping of text. Color properties control text, backgrounds, borders, and other painted details. Choose readable combinations first, then use consistent units and custom properties to keep them manageable. Custom properties connect these choices across a project. A declaration such as `--color-accent: #2563eb` gives a repeated color a clear name, while `var(--color-accent)` reads that value inside another declaration. Scoped overrides can change the same name for a component or theme. ## Syntax at a Glance ```css :root { --color-accent: hsl(217 91% 60%); --text-size: 1rem; } .notice { color: var(--color-accent); font: 600 var(--text-size) / 1.5 system-ui, sans-serif; text-wrap: pretty; } ``` This single rule shows the category's main parts. The custom properties name a color and text size, `var()` inserts them, the `font` shorthand sets weight, size, line height, and family, and `text-wrap` controls soft line wrapping. The child guides separate those choices and explain their trade-offs. ## Typography and Color Guides | Guide | What it covers | | [CSS Colors and Units](/programming/css/typography-and-color/colors-and-units/) | Color keywords, hex, modern `rgb()` and `hsl()` syntax, plus practical `px`, `rem`, `em`, percentage, and viewport units. | | [CSS Custom Properties](/programming/css/typography-and-color/custom-properties/) | Reusable values with `--name`, `var()`, fallbacks, scoped overrides, and a simple theme switch. | | [CSS Fonts and Text](/programming/css/typography-and-color/fonts-and-text/) | Font stacks, size, line height, weight, character spacing, word spacing, and text wrapping. | | [CSS Shadows](/programming/css/typography-and-color/shadows/) | `box-shadow`, `text-shadow`, and shape-aware `drop-shadow()` effects with reusable color syntax. | Follow the guides in the order that matches the work. Colors and units supply the raw values, fonts apply them to text, custom properties organize repeated choices, and shadows reuse color syntax to describe soft edges and depth. ## Related CSS Concepts Typography and color sit inside the cascade, so selector strength and source order can decide which declaration wins. Review [CSS specificity](/programming/css/selectors/specificity/) when a correct font or color rule appears crossed out in browser tools. Text also participates directly in every page layout. Width, padding, and display rules determine how much room a line receives before it wraps. The [CSS layout guide](/programming/css/layout/) covers those surrounding boxes, while the [CSS selectors guide](/programming/css/selectors/) explains how to target the elements that need a type or color rule. ## Frequently Asked Questions ### Which CSS color format should a beginner use? Beginners can use named CSS colors while learning, then hex, rgb(), or hsl() for precise values. All three support transparency, including eight-digit hex. Pick a consistent format within a project so colors are easier to compare. ### Should CSS font sizes use px or rem? Use rem for most CSS font sizes so they follow the root font size. Leave the root size tied to the browser default if you want to respect a reader’s text-size preference. Pixels remain useful for thin borders, and browser zoom can scale either unit. ### When should a CSS value become a custom property? Create a CSS custom property when a meaningful value repeats or needs to change by theme or component. Shared colors and spacing are useful candidates. Keep a one-off measurement in its declaration when naming it adds no benefit. ### Do CSS color and font settings inherit to child elements? CSS color and most font settings inherit by default. A declaration applied directly to a child takes precedence over an inherited value, regardless of the parent selector’s specificity. Properties such as margin and border do not normally inherit. ### What does currentColor do in CSS? CSS currentColor uses the element’s computed color value. A border, outline, or SVG fill can follow the text color without repeating the value. When used on color itself, currentColor acts like an inherited color. --- ## CSS Shadows: Box-Shadow, Text-Shadow, Drop-Shadow Effects URL: /programming/css/typography-and-color/shadows/ Cluster: Programming > Css A shadow tells the eye how far an element floats above the page, like a sheet of paper lifted from a desk. A tight, dark edge looks close to the surface. A wider, softer edge suggests more distance. Sites once generated shadow images on the server and attached them to elements; CSS now draws the effect directly. The [CSS guide](/programming/css/) covers the styling basics if you are just getting started. This guide is part of our [CSS typography and color](/programming/css/typography-and-color/) series. ```css .card { box-shadow: 0 0.5rem 1rem rgb(18 28 24 / 0.2); } ``` ```html
Shadow practice card
``` Save both examples together in one HTML file, with the CSS inside a ` ``` The [CSS guide](/programming/css/) covers table widths, cell spacing, wrapping, and small-screen layouts. ## How Do You Make an HTML Table Accessible? Make an HTML table accessible with meaningful `th` headers, appropriate `scope` values, and a concise `caption`. These describe the relationships a sighted reader follows across rows and columns. Test complex tables with the assistive technology your audience uses. HTML tables should represent meaningful rows and columns of data. Using table cells to position a sidebar, hero, or form adds relationships that the content does not have. CSS layout keeps visual placement separate from the document's data structure. ## Common Pitfalls & Debugging ### Headers Without Explicit Scope A browser can infer the direction of simple headers, although some assistive technology may not make the same association reliably. Add `scope="col"` or `scope="row"` to each `th`, then check that every data cell has the headers you expect. ### Uneven Cell Counts A short row leaves an empty slot, which can shift values away from their intended headings. Count the occupied slots in each row, including any slots claimed by `colspan` or `rowspan`. Developer tools make the mismatch easier to spot when the source looks balanced. ### Tables Used for Page Layout Layout tables often look acceptable on a wide screen, then read as meaningless data grids to assistive technology and become difficult to rearrange on narrow screens. Replace the layout table with semantic page elements and handle their placement in CSS. ### An Unexpected `tbody` in the DOM Browsers may insert a `tbody` around rows written directly inside `table`. JavaScript selectors such as `table > tr` then fail because each row is a child of the inserted body. Write `tbody` explicitly and target the structure that appears in the DOM. ## Frequently Asked Questions ### When should you use an HTML table instead of CSS Grid? Use an HTML table when the content has meaningful row and column relationships, such as prices, schedules, or results. Use CSS Grid when the columns exist only to arrange page sections or components visually. ### How do you make an HTML table responsive? Make a wide HTML table responsive by placing it in a container with overflow-x: auto, while keeping headers and cell relationships intact. Let text wrap where it stays readable, and check that keyboard users can reach and scroll the container. ### Are layout tables ever acceptable in modern HTML? Some HTML email templates still use layout tables because older mail clients support limited CSS. Keep the structure as simple as the email permits, add presentation roles where appropriate, and test it with the actual mail clients and assistive technologies your audience uses. ### Does every HTML table need thead, tbody, and tfoot? HTML tables do not require all three row groups. Use thead for header rows, tbody for the main data, and tfoot when you have a footer or summary. Explicit groups make styling and scripting easier even in a small table. ### Can an HTML table compare just two or three items? An HTML table can compare two or three items when the columns represent shared properties such as price and size. Choose a table because the data has row and column relationships, not because it reaches a minimum row count. ### Does a table need column widths, or can the browser size columns automatically? A browser sizes table columns automatically from cell content and available space unless CSS overrides it. Specify widths only when a design requires a particular proportion; automatic sizing keeps a data-driven table readable without extra CSS. --- ## HTML Spanning Cells: colspan and rowspan URL: /programming/html/tables/spanning-cells/ Cluster: Programming > Html When one cell needs to cover more than a single grid slot, `colspan` stretches it across columns and `rowspan` stretches it down across rows. This builds on [HTML table basics](/programming/html/tables/). If tags and nesting are new to you, start with the [HTML guide](/programming/html/). ## Syntax Spans extend the [normal table grid](/programming/html/tables/table-structure/). A positive value counts the occupied columns or rows. The special value `rowspan="0"` spans the remaining rows in the current row group. ```html covers two columns covers two rows ``` Both attributes take a whole number, the count of slots the cell should cover. A value of `2` means the cell takes its own slot plus the next one, `3` means three slots, and the default of `1` is just a normal cell. They work on `td` and `th` alike. ## How Does HTML colspan Work? HTML `colspan` makes one table cell occupy several columns. A header with `colspan="2"` covers two column positions, so that row needs one fewer separate cell than an unspanned row of the same width. ```html
Morning menu
Espresso 3.00
``` The heading row has a single `th` because that one cell covers both columns. The data row below it still has two cells, so the table stays two columns wide and the heading stretches the full width. ## How Does HTML rowspan Work? HTML `rowspan` makes one table cell occupy several rows. A cell with `rowspan="2"` fills its position in the current row and the next. Omit the corresponding cell from the lower row, because the spanning cell already occupies that position. ```html
Coffee Espresso
Cold brew
``` The "Coffee" header spans both rows, so it appears once on the left and reaches down past both drinks. The second row writes only its drink cell, because the spanned header already occupies that row's first column. ## How Do You Count Spanned Table Cells? Count every grid position occupied by a cell, including positions covered by a span from an earlier row. A cell with `colspan="2"` fills two columns. A cell with `rowspan="2"` also occupies a position in the next row, so that row needs one fewer cell there. ```html
Drinks Price
Espresso Hot 3.00
``` The header row has two cells but they account for three columns, because the first `th` spans two. The data row writes three plain cells to match, so the grid stays three columns wide and everything lines up. ## When Should HTML Table Cells Span? Reach for `colspan` or `rowspan` when one label describes a whole group of columns or rows, the way "Shift" labels both time columns in the schedule example below. That is different from using a span to patch over data that does not fit the grid cleanly, which is usually a sign that two separate tables, one for each shape, would serve the reader better than one table stretched to cover both. ## How Do You Label Spanning Table Headers? Label a spanning HTML table header with a scope value that matches its row, column, or group. Use explicit `headers` associations for more complex relationships, as explained in the [table accessibility guide](/programming/html/tables/table-headers-and-captions/). The `scope="colgroup"` value labels a column group defined with ``. The `scope="rowgroup"` value labels a row group such as ``. A span alone does not require either group value; choose the scope that describes the actual header relationship. Use the plain values for a header that labels one column or row, and reach for the group values when a header heads a group, which is often what a `colspan` or `rowspan` on a header means. A spanning header in a simple table can still carry a plain `scope="col"` or `scope="row"`, so match the value to what the header labels rather than applying the group values to every span. ## Example ```html
Weekly schedule
Day Shift
Monday Open 9 to 1
Close 1 to 5
``` This renders as a table that is three columns wide. The "Shift" header spans the two right columns, and the "Monday" header runs down both data rows on the left. Each data row carries the cells it still owns, so the columns stay aligned even though two cells cover extra ground. The two `` elements are what make `scope="colgroup"` mean something. That value only anchors a header to a real column group, so without a `` defining the two-column "Shift" group, assistive technology has nothing to tie the header to. The `scope="rowgroup"` header needs no extra markup, because the `` already forms its row group. ## Frequently Asked Questions ### Can an HTML table cell span both rows and columns? An HTML table cell can use colspan and rowspan together to cover a rectangle. Count the positions it occupies in every affected row before adding other cells. ### Do colspan and rowspan work on HTML th and td cells? HTML th and td cells both support colspan and rowspan. For a spanning header, choose scope or explicit headers associations that match the data it labels. ### What happens when HTML colspan exceeds the intended column count? A large HTML colspan can expand the table grid beyond the columns you intended, leaving gaps in other rows. The browser does not simply clamp it to the number of cells in another row. Check the total occupied columns across the table. ### Can CSS replace colspan and rowspan in an HTML table? CSS Grid can span visual tracks, but it does not replace colspan and rowspan in an HTML table. Use the HTML attributes for cell spans in tabular data. ### What happens to spanning HTML table cells on narrow screens? Spanning HTML table cells keep their spans on narrow screens. A horizontal scrolling container can keep the data readable without squeezing every column. Simplify the table if its header relationships are difficult to follow. --- ## HTML Table Headers and Captions URL: /programming/html/tables/table-headers-and-captions/ Cluster: Programming > Html Headers and a caption turn a raw grid of cells into a table that names its own columns, groups its rows, and reads correctly to a screen reader. This builds on [HTML table basics](/programming/html/tables/). If tags and nesting are new to you, start with the [HTML guide](/programming/html/). ## Syntax ```html
Table title
Heading
Data
``` ## How Do You Mark an HTML Table Header? The `th` element marks a cell as a header instead of plain data. Browsers render it bold and centered by default, but its job is semantic. It tells assistive technology that this cell labels the data cells near it. Swap a `td` for a `th` anywhere a cell is acting as a label rather than a value. ```html
Drink Price
Espresso 3.00
``` Headers do not have to sit only in the top row. A table can also have a header at the start of each row, for example a row that begins with a day name and then lists values for that day. ## Why Is a Bold Table Cell Not a Header? A `td` styled with bold, centered text can look exactly like a `th` on screen, and that similarity is the trap. Bold CSS does not give a `td` the header semantics of a `th`, so styling alone does not identify the cells it labels. Keep `th` for anything that is a header, and restyle it with CSS instead of swapping it for a `td` that only looks the part. ## How Do HTML Table Row Groups Work? HTML row groups separate header rows in `thead`, main data in `tbody`, and a summary in `tfoot`. These groups help organize markup and target CSS. They do not make headers sticky while scrolling; that requires CSS. ```html
Drink Price
Espresso 3.00
Total 3.00
``` Place `tfoot` after the body rows in current HTML. Older tutorials sometimes put it before `tbody`, but that is not the current content order. A short table of three or four rows does not need the full split. A bare header row followed by data rows is still valid, complete markup on its own. Reach for `thead`, `tbody`, and `tfoot` once a table grows long enough to scroll or picks up a summary row, because that is when the extra sectioning pays off. ## What Does scope Do in an HTML Table? The HTML `scope` attribute identifies which cells a `th` labels. Use `col` for a column and `row` for a row. Browsers can infer simple associations, but explicit scope makes your intention clearer. ```html
Day Hours
Monday Closed
``` Here the top cells scope to their columns and the day cell scopes to its row, so a screen reader can announce both the column and the row header for any value. Add `scope` to every header cell and the table stays readable when nobody can see the grid. Some screen readers make a reasonable guess on a small, simple table even without `scope`, but that guess breaks down once a table has more than one header row or mixes row headers with column headers. Write `scope` explicitly. ## How Do You Add an HTML Table Caption? The `caption` element gives the table a visible title, and it must be the very first child inside `table`, before any rows. It reads out as the table's name to assistive technology, so a reader who jumps between tables knows which one they landed on. ```html
Opening hours this week
Day Hours
Monday Closed
``` ## Example This builds on the [basic table structure](/programming/html/tables/table-structure/). For a header that covers several columns, see the [colspan and rowspan examples](/programming/html/tables/spanning-cells/). ```html
Weekend coffee prices
Drink Price
Espresso 3.00
Cold brew 4.50
``` This renders with the caption above the grid, a bold header row, and a bold header cell starting each data row. The markup associates each price with its row and column headers and gives the table a caption. Exact announcements depend on the screen reader, browser, and settings. ## Frequently Asked Questions ### How does an HTML table caption differ from a nearby heading? An HTML caption belongs to its table and can provide its accessible name. A nearby heading is separate unless you explicitly connect it, for example with aria-labelledby. Use caption for a short table title. ### When should an HTML table use the headers attribute? The HTML headers attribute lists the IDs of header cells associated with a data cell. Use it for relationships too complex for scope alone, or simplify the data into smaller tables. ### Is the HTML table summary attribute still valid? The HTML table summary attribute is obsolete. Put a short title in caption and any longer explanation in visible text near the table so all readers can use it. ### Can an HTML table caption be visually hidden? An HTML table caption can use a visually hidden CSS class that keeps it available to assistive technology. Do not use display: none or visibility: hidden when you want screen readers to access the caption. ### Should an HTML layout table have headers? An HTML layout table should not have data headers or a caption. Use CSS for web page layout. If an email template requires a layout table, role="presentation" can prevent it being exposed as a data table. --- ## HTML Table Structure: table, tr, td, and the Grid Model URL: /programming/html/tables/table-structure/ Cluster: Programming > Html An HTML table is built from three nested elements, `table` wraps the whole thing, `tr` is one row, and `td` is one data cell inside that row. This builds on [HTML table basics](/programming/html/tables/). If tags and nesting are new to you, start with the [HTML guide](/programming/html/). ## Syntax ```html
cell cell
``` The `table` element contains rows, often grouped inside `thead`, `tbody`, and `tfoot`. Each `tr` contains data cells written as `td` or header cells written as `th`. A caption and column groups can also appear inside the table. ## How Do HTML Table Columns Form? HTML table columns form from the positions of cells in each row. Without spanning, the first cell goes in the first column, the second in the second, and so on. A `col` element can describe a column for styling but does not hold its data. ```html
Ada Lovelace Analyst
Alan Turing Cryptographer
``` The names line up on the left and the roles on the right because every row uses the same cell order. ## Why Do HTML Table Cells Misalign? HTML table cells misalign when rows fill different column positions. Count normal cells and any slots occupied by [colspan or rowspan](/programming/html/tables/spanning-cells/) before adding another cell. Because columns come from position, a row with the wrong number of cells throws the whole grid off. If most rows have three cells and one row has two, that short row leaves a ragged gap where the missing cell would sit. The browser will not warn you, it just renders the hole. ```html
Espresso Hot 3.00
Cold brew 4.50
``` That second row only has two cells, so the table renders unevenly and the prices no longer line up under one column. Count your cells per row, and when a cell needs to cover more than one slot, reach for the spanning attributes instead of leaving a row short. ## When Should You Use an HTML Table? Use a table when each row is one record and each column is one property of that record, the way a price list or a weekly schedule works. If you could drop the content into a spreadsheet without losing anything, it belongs in a table. If the columns only exist to line up a few boxes, use CSS instead, usually flexbox or grid, which handles visual alignment without dragging tabular semantics into a layout problem. ## Why Do HTML Tables Need Header Cells? HTML table header cells identify what a row or column means. A table made only of `td` cells does not distinguish labels from values, leaving screen reader users without those header associations. Use `th` for labels, `scope` for their direction, and `caption` for the table's name. The [table headers guide](/programming/html/tables/table-headers-and-captions/) adds those to the basic grid. ## Example ```html
Monday Closed
Tuesday 9 to 5
Wednesday 9 to 5
``` This renders as three rows of two cells. The days form the left column and the hours form the right column, with each row contributing one cell to each. There is no header yet, so a screen reader reads it as a plain grid of data, which is the next thing you will want to fix. ## Frequently Asked Questions ### Do HTML table cells need a tr element? HTML td and th cells belong inside a tr row. A cell placed outside a row is invalid markup, even if the browser repairs it and displays a table. ### What do HTML colgroup and col elements do? HTML colgroup and col describe columns for a limited set of CSS properties, including width and background. They do not contain cell data or replace th headers. ### Can you nest one HTML table inside another? An HTML table can be nested inside a td or th cell, but separate tables are usually easier to read and navigate. Do not put a table directly inside another table without a containing cell. ### Why can an empty HTML table cell look wrong? An empty HTML table cell may have little height or hidden borders, depending on its CSS and the other cells in its row. Use padding and border rules for appearance. Add a label such as "Not available" only when that describes the missing value. ### Can an HTML table cell span rows or columns without spanning attributes? An HTML table cell needs colspan or rowspan to occupy multiple grid slots. CSS width can change its appearance, but does not make it span additional cells in the table model. --- ## Fetch, JSON, and the Network in JavaScript URL: /programming/javascript/async/fetch-json-and-the-network/ Cluster: Programming > Javascript Requesting data is the easy part, and the skills that matter come after it: telling a server error apart from a network failure, sending data back, and stopping a request that is taking too long. You need Node installed and nothing else. `fetch` is built into current Node, so every example here runs with `node app.js` from a folder containing a `package.json` with `{ "type": "module" }`. The same code is valid in a browser console, though CORS policy and different error wording can change what you see there. ## Your First Fetch The GitHub API needs no key for public data, which makes it a good first target. This asks for one repository and prints three fields: ```js const response = await fetch("https://api.github.com/repos/nodejs/node"); const data = await response.json(); console.log(data.full_name); console.log(data.default_branch); console.log(data.stargazers_count); ``` ```text // node output, the star count changes daily: // nodejs/node // main // 118828 ``` Two awaits, and they are waiting for different things. The first waits for the response headers to arrive, and the second waits for the body to finish downloading and be parsed. That split is why `response` is not your data. It is an object describing the answer, and you have to ask it for the body separately. ## The Check Almost Every Tutorial Skips Ask for a repository that does not exist and watch what does not happen: ```js const response = await fetch("https://api.github.com/repos/nodejs/not-a-real-repo"); console.log("ok:", response.ok); console.log("status:", response.status); console.log("statusText:", response.statusText); ``` ```text // node output: // ok: false // status: 404 // statusText: Not Found ``` No error was thrown. The request worked perfectly and the answer was a refusal, which from the network's point of view is a completely normal outcome. This is the single most common way a fetch bug hides. Without a check, the next line calls `.json()` on an error payload and your code carries on holding an object that is not what it thinks it is. The `ok` property is true only for statuses in the 200 to 299 range. Test it before touching the body: ```js async function getJson(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`Request failed: ${response.status} ${response.statusText}`); } return response.json(); } try { await getJson("https://api.github.com/repos/nodejs/not-a-real-repo"); } catch (error) { console.log(error.message); } ``` ```text // node output: // Request failed: 404 Not Found ``` Write that helper once and reach for it by default. Turning an unwanted status into a thrown error is what puts network failures back into the ordinary `try` and `catch` handling covered in the [promises guide](/programming/javascript/async/promises-and-async-await/). It is a starting point rather than a universal one. A 204 is `ok` and has no body at all, and plenty of successful responses are not JSON, so a helper used everywhere eventually has to check the status and the content type too. ## Turning a Response into Data The `.json()` method reads the whole body and parses it. It fails loudly when the body is not JSON, which happens more often than you would expect, because error pages are usually HTML: ```js const response = await fetch("https://example.com/"); console.log(response.status, response.headers.get("content-type")); await response.json(); ``` ```text // node output, stack trace trimmed: // 200 text/html // SyntaxError: Unexpected token '<', " todo.todo)); ``` ```text // node output. This is a shared demo API, so the rows you get back will differ: // https://dummyjson.com/todos?limit=2&skip=10 // 200 application/json; charset=utf-8 // [ 'a todo string', 'another todo string' ] ``` Every value is escaped on the way in, so a search term with spaces or punctuation survives the trip. Reading a header back uses `get` with the name, and the lookup ignores capitalization. Treat the rows themselves as illustration. That endpoint is a shared demo API whose contents change, which is a good habit to carry to any API you did not write. The `response.url` line is worth a habit of its own. It reports where the answer came from, which is not the address you asked for once a redirect has been followed. Headers you send go in the second argument, and they are how an API learns who you are and what you want back. The two you meet first are `Accept`, naming the format you want, and `Authorization`, carrying a key or a token. Browser-side, cookies follow a rule of their own. The default is `credentials: "same-origin"`, meaning cookies go only to the same scheme, host, and port that the page came from. The other two settings are `omit`, which never sends them, and `include`, which allows them cross-origin. Even then the cookie's own attributes and the browser's third-party cookie policy still apply, and the server has to return explicit origin and credentials headers before your script may read the response. That rule is behind a whole family of confusing bugs, where a logged-in page gets a 401 from an API on a different subdomain. Node's built-in `fetch` keeps no cookie jar of its own, so the same script authenticates differently in the two places unless you set the header yourself. ## Sending Data with POST Sending JSON takes three extra pieces: a method, a header saying what the body is, and the body itself as a string. A plain object cannot be a body, so `JSON.stringify` does the conversion. JSON is not the only option. A body can also be `FormData`, `URLSearchParams`, a blob, or plain text, and some of those set the content type for you. Writing to a real API usually needs a key, so this example uses a public demo API that simulates the write and answers with the record it would have created: ```js const response = await fetch("https://dummyjson.com/todos/add", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ todo: "Send the weekly report", completed: false, userId: 1, }), }); console.log(response.status, response.ok); console.log(await response.json()); ``` ```text // node output. The generated id varies, // and nothing is really saved: // 201 true // { // id: , // todo: 'Send the weekly report', // completed: false, // userId: 1 // } ``` The 201 status means created, and it counts as ok. An API that needs to know who you are usually wants one more header, most often `Authorization` holding a key or a token, though cookies and signed URLs are common too. Keep that key on a server. Anything in front-end JavaScript is visible to anyone who opens the browser tools, so a key shipped to the page is a key you have published. Two details catch people out. Forgetting the `Content-Type` header makes many servers reject the body as unreadable, and passing an object instead of a string sends the useless text `[object Object]`. ## Two Kinds of Failure You have seen the first kind: the server answered, and the answer was bad. The second kind is when no usable answer reaches your code, and that is the kind that rejects: ```js try { await fetch("https://this-host-does-not-exist.example/data"); } catch (error) { console.log(error.constructor.name); console.log(error.message); console.log("cause:", error.cause?.code); } ``` ```text // node output: // TypeError // fetch failed // cause: ENOTFOUND ``` The message is famously unhelpful. In Node the detail lives in `error.cause`, so log that too: `ENOTFOUND` means the hostname did not resolve, and `ECONNREFUSED` means nothing was listening. Browsers word this differently and often give you a bare `TypeError` with nothing attached. The standard promises the `TypeError`, not the message or the code. In a browser the same rejection covers being offline, a blocked request, and a CORS refusal. Real code needs both guards, because the two families arrive through different doors: ```js try { const response = await fetch(url); if (!response.ok) { console.log("the server refused:", response.status); return; } const data = await response.json(); console.log(data); } catch (error) { console.log("no usable response reached the code:", error.message); } ``` ## Timeouts and Cancelling a Request A request with no deadline of your own can wait far longer than your product should. Something else will eventually give up, whether the browser, Node, a proxy, or the server, but not on your schedule. `AbortSignal.timeout` is the shortest fix: ```js try { await fetch("https://api.github.com/repos/nodejs/node", { signal: AbortSignal.timeout(1), }); } catch (error) { console.log(error.name); console.log(error.message); } ``` ```text // node output: // TimeoutError // The operation was aborted due to timeout ``` The timeout is set to one millisecond so a timeout is all but certain. Use something realistic, such as 8000, in real code. When you need to cancel on purpose rather than on a clock, create an `AbortController` and keep it. Calling `abort` stops your side of the request wherever it has got to, though the server may already have done the work: ```js const controller = new AbortController(); setTimeout(() => controller.abort(), 10); try { await fetch("https://api.github.com/repos/nodejs/node", { signal: controller.signal, }); } catch (error) { console.log(error.name, "|", error.message); } ``` ```text // node output: // AbortError | This operation was aborted ``` Check for `error.name` being `AbortError` in your `catch` block and stay quiet about it. A cancellation you asked for is not a failure the reader should see. Browser-side, this is also how you cancel a request whose result nobody wants any more, such as a search whose box has changed since. The [timers and races guide](/programming/javascript/async/timers-ordering-and-race-conditions/) covers that pattern in full. ## Pitfalls and Debugging SyntaxError: Unexpected token `<`. The body was HTML, not JSON. Print `response.status` and `response.headers.get("content-type")` before parsing, and read the URL again. TypeError: fetch failed. Node's wording for a request that produced no usable response. Log `error.cause` for the real reason, then check the hostname, the port, and whether the server is running. A browser words this differently and may tell you far less. The data is undefined and no error appeared. The field you read does not exist on the object you got. Log the whole parsed body once before reaching into it, since a parsed response is only as correct as the request that produced it. Blocked by CORS policy. The server did not permit your page to read the response. This is not fixable in front-end code, so route the call through a server you control, or use an API that allows browser requests. A POST that arrives empty. The two most common causes are a body that was an object rather than a string, and a missing `Content-Type` header. Routing, middleware, proxies, and size limits can produce the same symptom, so check the request in the Network tab before rewriting the code. Everything works locally and fails on the deployed site. Usually a URL. A relative path such as `/api/notes` resolves against whatever domain the page is on, which is rarely the same in both places. The request is fine but you cannot see it. Open the browser tools, choose the Network tab, and reload. It shows the URL, the status, the headers sent, and the exact body returned, which settles most arguments in seconds. ## Frequently Asked Questions ### Why does fetch not throw on a 404? Because the request succeeded. The browser asked, the server answered, and the answer was a 404 page. An HTTP error status on its own never rejects; fetch rejects for network failures, aborts and timeouts, bad request options, and browser policy refusals such as CORS. That design is why checking response.ok yourself is not optional. ### Do you still need axios? Not for ordinary requests. Fetch is built into every current browser and into Node, so a plain project needs no dependency at all. Libraries still earn their place on larger apps for interceptors and upload progress, which fetch does not provide. Retries are usually a plugin rather than a default, in axios included. ### What is a CORS error and can you fix it in your code? It means the server did not give the browser permission to share its response with your page. You cannot grant yourself access by adding an Access-Control-Allow header to the request. Removing a custom request header sometimes avoids a preflight; otherwise the fix belongs to the API owner, or to a small server of your own. ### Should you trust the shape of the JSON you get back? No. A parsed response is whatever the server sent, and reading a field that is not there gives you undefined rather than an error, which surfaces much later as a confusing bug. Check the fields you depend on, or validate the whole object before using it. ## Related - [JavaScript Async](/programming/javascript/async/) for the full topic overview - [Promises and async/await](/programming/javascript/async/promises-and-async-await/) for the promise machinery every example here uses - [Timers, Ordering, and Race Conditions](/programming/javascript/async/timers-ordering-and-race-conditions/) for what happens when two requests overlap - [Objects, Arrays, and Destructuring](/programming/javascript/language-basics/objects-arrays-and-destructuring/) for reading the objects a parsed response gives you - [unknown, any, and Runtime Data](/programming/typescript/types/unknown-any-runtime-data/) for typing a response you did not write - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## JavaScript Async URL: /programming/javascript/async/ Cluster: Programming > Javascript This topic covers the JavaScript that runs later. Requests that take time, results that arrive out of order, and the syntax that lets you write all of it without nesting callbacks five levels deep. It is also where most half-finished projects stall. A form that submits twice, a search box that shows the wrong results, a spinner that never stops: all three can be async bugs, and none of them look like async bugs from the outside. Synchronous event and state logic can produce the same three symptoms. ## JavaScript Async Topics - [Promises and async/await](/programming/javascript/async/promises-and-async-await/) covers what a promise is, the `then` and `catch` pair, the `async` and `await` keywords that usually stand in for a `then` chain in everyday code, overlapping work with `Promise.all`, and where a thrown error ends up. - [Fetch, JSON, and the Network](/programming/javascript/async/fetch-json-and-the-network/) covers requesting data over HTTP, the `response.ok` check that almost every tutorial skips, turning a response into an object, sending data with POST, and cancelling a request that is taking too long. - [Timers, Ordering, and Race Conditions](/programming/javascript/async/timers-ordering-and-race-conditions/) covers `setTimeout` and `setInterval`, the order things print, the stale response problem that breaks search boxes, and the debounce and throttle patterns that stop a handler firing on every keystroke. ## The Rule Everything Here Depends On One piece of JavaScript runs at a time, and it runs to the end before anything else gets a turn. When something slow starts, the API you called usually hands back a placeholder, your code carries on, and the engine returns to your callback once there is a result. Not every API is shaped that way. Some return a promise, some take a callback, and some hand back a handle or nothing at all. The pattern below is the one they all share. ```js console.log("1: asking for the data"); setTimeout(() => console.log("3: the data arrived"), 0); console.log("2: carrying on"); ``` ```text // node output: // 1: asking for the data // 2: carrying on // 3: the data arrived ``` The delay was zero and the third line still printed last. That is not a timing accident. Work handed off this way is queued, and the queue is only read once the currently running piece of code has finished and given the engine back. Every subject in this topic follows from that one behavior. Promises exist to give you a handle on a value that has not arrived. Race conditions exist because two of those handles can come back in an order you did not choose. ## How the Three Guides Fit Together The first guide is the vocabulary. A promise, the three states it can be in, and the `await` keyword that lets you write waiting code in a straight line. Read it first, because the other two use `await` in nearly every example without pausing to explain it. The second guide is the reason most readers came. Fetching data from an API is one of the most common async jobs in a real project, and it is also where the failure handling is easiest to get wrong. The third guide is what happens once you have more than one of those calls in flight. Ordering, cancellation, and the small set of patterns that keep a fast typist from seeing results for a search they already changed. Two neighboring subjects sit outside this topic. Attaching a handler to a button or a form belongs to the DOM rather than to async work, and the language basics such as functions and closures are covered in [JavaScript Language Basics](/programming/javascript/language-basics/). ## Four Requests, One Wait The most expensive async mistake in everyday code is not a syntax error. It is waiting for four things one after another when nothing required you to. ```js // urls holds four endpoints; getJson wraps fetch and returns // the parsed body. Each request takes roughly 300ms. // sequential: each await holds the next request back const sequential = []; for (const url of urls) { sequential.push(await getJson(url)); } // overlapping: all four start, then you wait once const overlapping = await Promise.all(urls.map(getJson)); ``` ```text // node output, timed: // sequential: 1218ms // overlapping: 312ms ``` When all four requests fulfill, both produce the same values. The loop asks for the second URL only once the first has come back, so its total is roughly the four waits added together. The second version splits the job in two. `urls.map(getJson)` starts all four requests, and `Promise.all` watches those promises and waits for them, so the total is roughly the slowest single request. Choosing between them is a question about dependency rather than speed. If the second request needs a value from the first, the loop is correct and the delay is real work. If the four calls know nothing about each other, the loop is only spending time. One detail worth carrying into the guides: `Promise.all` rejects as soon as any input rejects, and the requests already in flight are not cancelled by that. When you want every outcome regardless of failures, `Promise.allSettled` waits for all of them and reports each result separately. The [promises guide](/programming/javascript/async/promises-and-async-await/) works through both shapes with error handling attached, and the [timers and races guide](/programming/javascript/async/timers-ordering-and-race-conditions/) covers what happens when results come back in an order you did not plan for. ## Telling an Async Bug from an Ordinary One The three symptoms named at the top of this page are the ones people misdiagnose most often. Three questions gather clues faster than reading the code again. First, does the wrong behavior survive when the slow part is removed? Swap the request for a value returned immediately, and a bug that stays put suggests the event handling or the state rather than the waiting. Second, does the output order change between runs? Varying output can indicate an ordering problem, though plenty of async bugs are perfectly repeatable and plenty of ordinary ones are not. Third, does it only show up on a slow connection or a tired machine? Throttling the network in your browser tools may expose a race that a fast local server was hiding. Treat all three as evidence rather than a verdict, because the fixes have nothing in common. A missing `await`, a missing `response.ok` check, and a stale response are separate problems wearing the same symptom. ## Common Pitfalls - Treating a failed request as an error: `fetch` resolves happily on a 404 or a 500, so code without an `response.ok` check reads a server error page as data. The [fetch guide](/programming/javascript/async/fetch-json-and-the-network/) covers the two failure families and how to tell them apart. - Forgetting `await` and wrapping the call in `try`: the function returns before anything fails, the `catch` block never runs, and the failure surfaces later, somewhere you were not looking. What the host does with it then varies. The [promises guide](/programming/javascript/async/promises-and-async-await/) shows exactly what that looks like. - Awaiting inside a loop when the calls do not depend on each other: four independent requests take roughly the sum of their durations rather than the longest of them. `Promise.all` is the fix, and the same guide covers when sequential really is correct. - Rendering whatever comes back last: a slow earlier request can land after a fast later one and overwrite it. The [timers and races guide](/programming/javascript/async/timers-ordering-and-race-conditions/) covers the two guards worth knowing. - Leaving an interval running: `setInterval` keeps firing until something calls `clearInterval`, whether or not the thing that started it is still on screen. The same guide covers cleanup. ## Common Questions ### Which guide should you read first? Start with promises and async/await, because the other two assume you can read an async function without stopping. Fetch comes next, since a network call is where most people meet a promise for the first time. Timers and races come last, because they are about what happens when two of those calls overlap. ### Why does async code feel harder than the rest of JavaScript? Because the line order on screen stops matching the order things happen. Synchronous statements run in order and finish before anything else gets a turn, and async work does not: a function can return before its result exists. These three guides are mostly about learning to read that gap rather than fight it. ### Does async make code run in parallel? Not in the sense of two pieces of your code executing at the same moment. What it buys is the ability to start slow work, hand control back, and pick the result up later. Four network requests can be in flight at once, and it is the waiting that overlaps rather than your JavaScript. ### Do you still need callbacks if you have await? Yes, and not as a fallback. Event listeners, setTimeout, and array methods such as map all take callbacks, and none of them are going away. What await usually replaces is an explicit then chain, and try/catch is what replaces catch. A callback-only API still needs a callback, or a wrapper that turns it into a promise. ### Should every async function have a try/catch? No, and wrapping every one of them tends to hide failures rather than handle them. A catch belongs where the code can do something useful, which is often further up the call chain. What matters is that somebody handles it, because a rejection nothing catches is easy to lose. ### Where do you run the examples on these pages? Node runs almost all of them, including the fetch examples, since fetch is built into current Node. Save a file such as app.js next to a package.json containing type module, then run node app.js. The few examples that need a real web page are marked as browser-side where they appear. ## Continue Learning JavaScript - Return to the [JavaScript guide](/programming/javascript/) for where the language fits, the full learning path, and what to build first. - Read [JavaScript Language Basics](/programming/javascript/language-basics/) for the values, functions, and closures every example here assumes. - Read [TypeScript Types](/programming/typescript/types/) when you want a compiler to tell you what shape an API response has before you use it. --- ## Promises and async/await in JavaScript URL: /programming/javascript/async/promises-and-async-await/ Cluster: Programming > Javascript The first hurdle with async code is reading an async function without losing the thread. The rest builds on that: running three requests at once instead of one after another, and putting a `try` block in the one position where it catches the failure. You need Node installed to run the examples, and nothing else. Save each snippet as a file such as `app.js` next to a `package.json` containing `{ "type": "module" }`, then run `node app.js`. ## What a Promise Actually Is A promise is an object standing in for work that has not finished yet, and sometimes for work that already has. It has three states. It starts pending, and then it either fulfills with a value or rejects with a reason, once and permanently. You can watch two of the three in one run. This example makes a promise that settles after a tenth of a second, then logs the object before and after: ```js const slowValue = new Promise((resolve) => { setTimeout(() => resolve("done"), 100); }); console.log(slowValue); slowValue.then((value) => { console.log("settled with", value, slowValue); }); ``` ```text // node output: // Promise { } // settled with done Promise { 'done' } ``` The first line printed while the value was still missing. That gap is the whole point of a promise, and it is also why logging a function call directly so often prints `Promise { }` instead of the answer you expected. The word settled covers both endings. A settled promise never changes again, so calling `resolve` twice does nothing the second time, and a rejection cannot be talked back into a value. Most of the time you will not write `new Promise` at all, because browser and Node APIs hand you promises already made. Its job is to build a promise around something that does not produce one, and the most common case is an older function that takes a callback: ```js import { readFile } from "node:fs"; function readFilePromise(path) { return new Promise((resolve, reject) => { readFile(path, "utf8", (error, contents) => { if (error) reject(error); else resolve(contents); }); }); } console.log(await readFilePromise("./package.json")); try { await readFilePromise("./missing.json"); } catch (error) { console.log("rejected with:", error.code); } ``` ```text // node output: // { "type": "module" } // // rejected with: ENOENT ``` That is the whole pattern. Call `resolve` where the old code succeeded, call `reject` where it failed, and everything downstream can use `await`. You rarely need to write it by hand for Node's own APIs. Many of them ship a promise version, which for the file system lives at `node:fs/promises`. Plenty of others stay event-based or stream-based, and those still want a wrapper or a listener. ## Then, Catch, and Finally Before `async` existed, you read the value out of a promise by handing it a callback. That style is still everywhere in working code, so it is worth being able to read: ```js function delay(ms, value) { return new Promise((resolve) => setTimeout(() => resolve(value), ms)); } delay(50, "first") .then((value) => { console.log("then got", value); return value.toUpperCase(); }) .then((value) => console.log("second then", value)) .catch((error) => console.log("never runs here", error)); ``` ```text // node output: // then got first // second then FIRST ``` Each `then` returns a new promise, which is why they chain. Whatever you return from one callback becomes the value the next one receives, and returning a promise from inside a `then` waits for it rather than nesting it. Failures work the same way in reverse. A rejection skips every `then` that only handles success, until something offers to handle it, and `finally` runs either way: ```js function failAfter(ms) { return new Promise((resolve, reject) => { setTimeout(() => reject(new Error("boom")), ms); }); } failAfter(10) .then(() => console.log("skipped")) .catch((error) => console.log("caught:", error.message)) .finally(() => console.log("finally ran")); ``` ```text // node output: // caught: boom // finally ran ``` Note that `finally` receives no argument. It is for cleanup that has to happen either way, such as hiding a spinner, not for inspecting what happened. `catch` is not the only place a rejection can be handled. A `then` takes a second callback for exactly that, and a handler that returns normally puts the chain back on the success path: ```js failAfter(10) .then( (value) => console.log("skipped", value), (error) => { console.log("handled by the second argument:", error.message); return "recovered"; }, ) .then((value) => console.log("next then still runs, with:", value)) .catch(() => console.log("never reached")); ``` ```text // node output: // handled by the second argument: boom // next then still runs, with: recovered ``` So a rejection travels past handlers that only deal with success, and stops at the first one willing to take it. You can recover, as well as report. ## Async Functions and Await The `async` and `await` keywords let you write the same thing as ordinary top-to-bottom code. Put `async` on the function and `await` in front of any promise: ```js function delay(ms, value) { return new Promise((resolve) => setTimeout(() => resolve(value), ms)); } async function loadReport() { const owner = await delay(50, "Dana"); const title = await delay(50, "Weekly numbers"); return `${title}, by ${owner}`; } console.log(await loadReport()); ``` ```text // node output: // Weekly numbers, by Dana ``` Two rules make this behave predictably. An `async` function always returns a promise, whatever its body returns, and `await` suspends only the async function it appears in, or the module when it is used at the top level. That second rule matters more than it sounds. The rest of the program carries on while an async function is paused, which is exactly how two calls started one after the other end up overlapping. You can use `await` at the top level of an ES module, as the example above does. Inside a plain function that is not marked `async`, it is a syntax error. Two smaller behaviors round the picture out. First, awaiting something that is not a promise still gives you the value, and still hands control back before resuming. ```js async function readValue() { console.log("1: inside, before the await"); const value = await 42; console.log("3: await gave back", value); } readValue(); console.log("2: the line after the call"); ``` ```text // node output: // 1: inside, before the await // 2: the line after the call // 3: await gave back 42 ``` Line three came last even though 42 was sitting right there. `await` always suspends, whatever it is given, which is why a helper can return either a value or a promise without its callers needing to know. Second, a promise you deliberately do not wait for still needs a `catch`, or its failure has no owner. ```js async function trackView() { throw new Error("analytics endpoint refused"); } trackView().catch((error) => console.log("logged and moved on:", error.message)); console.log("the page carried on rendering"); ``` ```text // node output: // the page carried on rendering // logged and moved on: analytics endpoint refused ``` This is the fire-and-forget pattern, and the `catch` on the end is not optional. It is the only thing that owns a failure nobody is waiting for. The rendering line printed first because the `catch` callback is queued rather than run on the spot. Nothing waits for the tracking call, so the page carries on and the report arrives afterwards. ## Sequential Work Against Promise.all Awaiting one line after another is correct when each step needs the one before it. When the steps are independent, it wastes the wait. This example times both shapes with the same three tasks: ```js function delay(ms, value) { return new Promise((resolve) => setTimeout(() => resolve(value), ms)); } console.time("sequential"); const a = await delay(300, "a"); const b = await delay(300, "b"); const c = await delay(300, "c"); console.timeEnd("sequential"); console.time("overlapping"); const all = await Promise.all([delay(300, "a"), delay(300, "b"), delay(300, "c")]); console.timeEnd("overlapping"); console.log(all); ``` ```text // node output, give or take a few milliseconds: // sequential: 906.855ms // overlapping: 302.246ms // [ 'a', 'b', 'c' ] ``` Three hundred milliseconds against nine hundred, for identical work. The three waits overlapped, which is not the same as running on three processors: `Promise.all` does not start anything, it watches promises that are already running. It gives back an array of results in the same order you passed them, regardless of which finished first. The same waste hides inside loops, where it is much harder to spot. Awaiting inside `for` makes each pass wait for the last one: ```js const ids = [1, 2, 3, 4]; console.time("loop"); const results = []; for (const id of ids) { results.push(await delay(200, id)); } console.timeEnd("loop"); console.time("overlapping"); const faster = await Promise.all(ids.map((id) => delay(200, id))); console.timeEnd("overlapping"); ``` ```text // node output: // loop: 807.979ms // overlapping: 201.143ms ``` Reach for the loop deliberately, not by habit. Sequential is right when each request needs the previous answer, and when a server would rate-limit you for firing everything at once. One caution on `Promise.all`: it rejects as soon as any single promise rejects, and it does not cancel the others, so their work and their side effects carry on. That is right for all-or-nothing work and wrong for a dashboard, where one dead panel should not blank the other five. `Promise.allSettled` is the version for that case. A rejected input never rejects it, and it hands back a description of each outcome rather than the values directly: ```js function failAfter(ms, message) { return new Promise((resolve, reject) => { setTimeout(() => reject(new Error(message)), ms); }); } const results = await Promise.allSettled([ delay(10, "prices"), failAfter(10, "the news panel is down"), delay(20, "orders"), ]); for (const result of results) { if (result.status === "fulfilled") console.log("loaded:", result.value); else console.log("failed:", result.reason.message); } ``` ```text // node output: // loaded: prices // failed: the news panel is down // loaded: orders ``` Each entry carries a `status`, plus a `value` when it fulfilled or a `reason` when it rejected. The array is still in the order you passed it, so entry two belongs to the second request whatever happened to it. Two more combinators exist and you will meet them in other people's code. Neither waits for all of the inputs, and they disagree about what they are waiting for: ```js console.log(await Promise.race([delay(50, "the mirror"), delay(200, "the main server")])); try { await Promise.race([failAfter(10, "the mirror is down"), delay(50, "the main server")]); } catch (error) { console.log("race rejected:", error.message); } console.log(await Promise.any([failAfter(10, "the mirror is down"), delay(50, "the main server")])); ``` ```text // node output: // the mirror // race rejected: the mirror is down // the main server ``` `Promise.race` mirrors whichever input settles first, so a fast failure wins. `Promise.any` ignores rejections until something fulfills, and rejects with an `AggregateError` only once every input has failed. Four names, one question each. Do you need all of them, all of their outcomes, whatever settles first, or the first one that works. ## Where Errors Go Inside an async function, a rejected promise behaves like a thrown error, so ordinary `try` and `catch` work. The error travels up through every awaiting caller until something catches it: ```js async function loadUser() { throw new Error("user service is down"); } async function loadProfile() { const user = await loadUser(); return user.name; } try { await loadProfile(); } catch (error) { console.log("caught at the top:", error.message); } ``` ```text // node output: // caught at the top: user service is down ``` Nothing in `loadProfile` handled anything, and the message still arrived. Throwing inside an async function is the same act as returning a rejected promise, which is why the two styles interoperate. Now the version that catches nothing, and looks almost identical. The only difference is a missing `await`: ```js async function saveDraft() { throw new Error("disk is full"); } function save() { try { saveDraft(); console.log("saved"); } catch (error) { console.log("could not save:", error.message); } } save(); ``` ```text // node output: // saved // ...then the process crashes with the original error: // Error: disk is full // at saveDraft (file:///.../app.js:2:9) ``` It reported success. The call returned a promise immediately, the `try` block finished with nothing to catch, and the rejection surfaced afterwards with no owner. The rule that prevents this: `try` must wrap the `await`, not the call. A promise you never await and never `catch` is an unhandled rejection. Node's default is to turn that into an uncaught exception, which ends the process, though flags and a rejection handler can change it. Browsers report it and carry on. There is a middle position between catching and ignoring, and it is usually the right one. Catch the failure where you know something useful about it, add that context, and throw again so the caller still finds out: ```js async function loadPrices() { try { throw new Error("connection reset"); } catch (error) { throw new Error("could not load prices", { cause: error }); } } try { await loadPrices(); } catch (error) { console.log(error.message); console.log("caused by:", error.cause.message); } ``` ```text // node output: // could not load prices // caused by: connection reset ``` The `cause` option keeps the original error attached instead of replacing it. That matters because the readable message and the diagnostic detail are usually two different things, and a rewritten error that drops the original throws away the half you need at three in the morning. Catch where you can act, and let everything else travel. A `catch` block that logs and then carries on regardless is how a failure becomes an empty screen, with the only trace of it sitting in a console nobody opened. ## Pitfalls and Debugging The console prints `Promise { }`. You logged the call rather than its result. Add `await` in front of it, inside an async function or at the top level of a module. An unhandled rejection ends the Node process with code 1. Something rejected with nobody watching. Node prints the original error and its stack, so read that, then find the call that has no `await` and no `.catch`. SyntaxError: await is only valid in async functions. The `await` is inside a plain function, and very often that function is a callback you passed to something else. Mark that callback `async` rather than the outer function. That fixes the syntax only. If the API ignores the promise the callback now returns, as `forEach` does, nothing waits for it and the rejection still needs an owner. An `await` inside `forEach` that does not wait. `forEach` ignores the promise its callback returns, so the loop finishes instantly and the work runs unsupervised. Use `for...of` when you want each step to wait, or `Promise.all` with `map` when you do not. A `catch` block that reports a useless error. Log the whole error object rather than just its message, so the stack survives, and check `error.cause` as well. `cause` is optional and holds whatever the thrower put there, but when it is present it usually holds the real problem. Everything works but takes far too long. Wrap the suspect section in `console.time` and `console.timeEnd` as the examples above do. A total that is a neat multiple of one request's duration suggests the calls are running one at a time, though retries, queues, and rate limits can look the same. Mixing `await` with a `then` chain on the same promise. It works, but nobody can read it. Pick one style per function. ## Frequently Asked Questions ### Should you use then or await? Use await for anything you would otherwise read top to bottom, because the result is a straight line of code with ordinary try/catch around it. Reach for then when you are attaching a small follow-up to a promise you are not waiting on, such as a background log. End that chain with a catch, because then alone owns no rejection. ### Does await block the whole page? No. Inside a function it suspends that async function, and at the top level of a module it suspends that module and anything importing it. Either way the rest of the program keeps running, which is why two async functions started one after the other can overlap. What does block a page is a long synchronous loop. ### When should you use Promise.allSettled instead of Promise.all? Use allSettled when one failure should not cancel your interest in the others, such as loading six independent dashboard panels. A rejected input does not reject it. You get an array describing each result as fulfilled or rejected. Promise.all is right when the work is all-or-nothing and a single failure makes the rest useless. ### Why does my async function return a promise instead of a value? Because that is what async means. Every async function returns a promise, even one whose body has no await and returns a plain number. The caller has to await it or attach a then. Logging the call without either is the most common way people end up printing Promise { pending } to the console. ## Related - [JavaScript Async](/programming/javascript/async/) for the full topic overview - [Fetch, JSON, and the Network](/programming/javascript/async/fetch-json-and-the-network/) for the promises you will meet most often in practice - [Timers, Ordering, and Race Conditions](/programming/javascript/async/timers-ordering-and-race-conditions/) for what happens when two of these calls overlap - [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/) for the callbacks `then` takes and the state they capture - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## Timers, Ordering, and Race Conditions in JavaScript URL: /programming/javascript/async/timers-ordering-and-race-conditions/ Cluster: Programming > Javascript Timing bugs show up in three familiar ways: a delayed callback runs later than you asked, a search box shows results for a word you already deleted, and a handler fires on every keystroke. You need Node installed to run the examples. Two of them are browser-side and are marked where they appear, because they involve a page that can disappear while your code is still running. ## setTimeout and setInterval These are the two timers you meet first. `setTimeout` runs a callback once after a delay, and `setInterval` keeps running it until something stops it. Both hand back a handle, and passing that handle to the matching clear function is the only way to cancel. What the handle is depends on the host. Browsers give you a number and Node gives you a `Timeout` object, so keep whatever you were given rather than assuming it is an id. ```js let ticks = 0; const start = Date.now(); const timerId = setInterval(() => { ticks += 1; console.log("tick", ticks, "at", Date.now() - start, "ms"); if (ticks === 3) clearInterval(timerId); }, 100); ``` ```text // node output, give or take a few milliseconds: // tick 1 at 101 ms // tick 2 at 204 ms // tick 3 at 305 ms ``` Notice the drift. Each tick arrived a little later than its slot, and how much lateness accumulates depends on the host and on what else is running. Either way an interval is a poor clock, so when elapsed time matters read it from `Date.now` as the example does rather than counting ticks. An interval that nobody clears runs forever. In Node it also keeps the process alive, and in a browser it keeps firing long after whatever started it has gone from the screen. ## What Actually Runs First A delay of zero does not mean now. This is the ordering that surprises everyone once: ```js console.log("1: top of the file"); setTimeout(() => console.log("4: setTimeout 0"), 0); Promise.resolve().then(() => console.log("3: promise callback")); console.log("2: bottom of the file"); ``` ```text // node output: // 1: top of the file // 2: bottom of the file // 3: promise callback // 4: setTimeout 0 ``` Two rules explain the whole result. Ordinary code runs to the end first, and then the promise callbacks already waiting are taken before any timer callback. The second rule has a sharper edge than it looks. Promise callbacks are drained completely, including any that get added while draining, before a single timer callback is allowed in: ```js setTimeout(() => console.log("task A"), 0); setTimeout(() => console.log("task B"), 0); Promise.resolve() .then(() => console.log("microtask 1")) .then(() => console.log("microtask 2")) .then(() => console.log("microtask 3")); ``` ```text // node output: // microtask 1 // microtask 2 // microtask 3 // task A // task B ``` The three promise callbacks were queued one at a time, each by the one before it, and all three still ran before a timer that had been waiting since the first line. The rule is about what is already queued, not about promises in general. Awaiting something that has already settled resumes before a zero-delay timer, because the resumption is queued at once. Awaiting something that settles later does not: ```js async function waitForSlow() { console.log("2: about to await a promise that settles in 20ms"); await new Promise((resolve) => setTimeout(resolve, 20)); console.log("4: the await resumed"); } setTimeout(() => console.log("3: setTimeout 0"), 0); waitForSlow(); console.log("1: end of the file"); ``` ```text // node output: // 2: about to await a promise that settles in 20ms // 1: end of the file // 3: setTimeout 0 // 4: the await resumed ``` The zero-delay timer won, because nothing had been queued on its behalf yet. A promise callback only outranks a timer once it is waiting in the queue, and a promise that has not settled has queued nothing at all. The other way is simpler. A timer cannot interrupt code that is still running, so a slow loop delays every callback behind it: ```js const start = Date.now(); setTimeout(() => console.log("waited", Date.now() - start, "ms"), 100); const end = Date.now() + 300; while (Date.now() < end) {} console.log("blocking loop finished"); ``` ```text // node output: // blocking loop finished // waited 302 ms ``` The delay was 100 and the callback ran after 302. The timer became eligible somewhere around its threshold and had nowhere to run, which is why a delay is a minimum rather than a promise. ## The Stale Response Problem Two requests start, the second finishes first, and then the first one lands on top of it: ```js function search(term, ms) { return new Promise((resolve) => { setTimeout(() => resolve(`results for ${term}`), ms); }); } let shown = null; async function runSearch(term, ms) { const results = await search(term, ms); shown = results; console.log("rendered:", results); } runSearch("rep", 300); runSearch("report", 50); setTimeout(() => console.log("final state:", shown), 400); ``` ```text // node output: // rendered: results for report // rendered: results for rep // final state: results for rep ``` The reader typed `report` and the screen ends up showing results for `rep`. Nothing threw, no request failed, and the code reads correctly line by line. The fix is to record which request is the current one and let the others fall on the floor. One shared counter outside the function, one private copy inside each call: ```js let latestRequestId = 0; async function runSearch(term, ms) { const requestId = ++latestRequestId; const results = await search(term, ms); if (requestId !== latestRequestId) { console.log("discarded stale:", results); return; } console.log("rendered:", results); } runSearch("rep", 300); runSearch("report", 50); ``` ```text // node output: // rendered: results for report // discarded stale: results for rep ``` The two variables do different jobs, and the difference is the whole trick. Each call gets its own `requestId`, while `latestRequestId` is a single variable every call shares. A closure captures the variable itself, not a copy of its value, so the comparison after the `await` reads whatever `latestRequestId` holds at that moment. The [closures guide](/programming/javascript/language-basics/functions-callbacks-and-closures/) covers that live binding in full. When the work is a real network request, cancelling beats discarding. Keep the `AbortController` for the request in flight and abort it when a new one starts, so the browser stops pulling down a result you are going to throw away. Bytes already transferred and work the server has already done are not refunded. The [fetch guide](/programming/javascript/async/fetch-json-and-the-network/) covers the setup. ## Debounce and Throttle The guard above handles the results. These two patterns reduce how many requests start in the first place, and they are not interchangeable. Debounce waits for a pause. Every new call cancels the pending one, so a burst of typing produces a single call at the end: ```js function debounce(callback, waitMs) { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => callback(...args), waitMs); }; } const save = debounce((text) => console.log("saved:", text), 200); save("r"); save("re"); save("rep"); save("report"); ``` ```text // node output: // saved: report ``` Four calls, one save, and the one that survived is the last. That is what you want for search suggestions, autosave, and anything else where only the final value matters. Throttle is the opposite trade. It lets at most one call through per interval while the activity continues, which suits scroll and resize handlers where you do want updates during the movement: ```js function throttle(callback, intervalMs) { let lastRun = 0; return (...args) => { const now = Date.now(); if (now - lastRun < intervalMs) return; lastRun = now; callback(...args); }; } const report = throttle((position) => console.log("position", position), 100); let position = 0; const timerId = setInterval(() => { report(++position); if (position === 25) clearInterval(timerId); }, 20); ``` ```text // node output, one representative run. // The exact positions and the number of calls shift with timing: // position 1 // position 6 // position 11 // position 16 // position 21 ``` Twenty-five events, a handful of calls. This version fires on the leading edge and has no trailing call, so the last event in a burst can be dropped. Both helpers work because the returned function closes over a variable that survives between calls, the same live binding the stale-request guard relies on. One rule about placement. Create the debounced function once and reuse it, because building a new one on every keystroke gives each call its own private timer and cancels nothing. ## Cleaning Up When the Owner Disappears This section is browser-side. On a real page, the thing that started a timer or a listener can be removed while the timer is still counting, and nothing stops it on your behalf. Whatever starts something has to be able to stop it. Keep the timer id, and keep the `AbortController`, so that both have an owner: ```js // browser-side const controller = new AbortController(); const timerId = setInterval(refreshPrices, 5000); button.addEventListener("click", refreshPrices, { signal: controller.signal }); function teardown() { clearInterval(timerId); controller.abort(); } ``` The `signal` option is worth the habit. One `abort` call removes every listener registered with that signal, which beats matching each `removeEventListener` to the exact function reference it was added with. In React, `teardown` is what you return from `useEffect`, and the framework calls it when the component is removed or before the effect is set up again. In development, Strict Mode also runs an extra setup and cleanup cycle on purpose, which is a good way to find out whether your cleanup works. Other frameworks name the hook differently and the obligation is identical. Skipping it produces a recognizable class of bug. A timer that fires against a screen that is gone, a request whose result is written into nothing, or a counter that runs twice as fast because the old interval is still going alongside the new one. ## Pitfalls and Debugging Calling the function instead of passing it. `setTimeout(save(), 1000)` runs `save` now and hands its return value to the timer. Node rejects that with `TypeError: The "callback" argument must be of type function`, while a browser accepts the `undefined` in silence, so the only symptom there is `save` running too early. The fix is `setTimeout(save, 1000)`, or `setTimeout(() => save(draft), 1000)` when arguments are involved. An interval that speeds up. Something started a second one without clearing the first, usually a component that mounted twice. Store the id and clear it before starting another. A debounce that never debounces. The debounced function is being created inside the handler, so each keystroke gets a fresh timer. Create it once, outside. The screen shows results for the wrong input. The stale response problem. Add the request-id guard, or abort the previous request before starting the next. An error inside a timer callback that no `try` block catches. The callback runs long after the code that scheduled it, so the surrounding `try` is finished. Put the handling inside the callback. A Node script that will not exit. A pending interval keeps the process alive. Clear it, or call `unref` on the returned timer when the work is optional. Timers running far slower than expected in a browser. Background tabs are throttled deliberately, to at most once per second and sometimes much less often or suspended entirely. Do not build anything that depends on a background tab keeping accurate time. ## Frequently Asked Questions ### Is setTimeout with zero the same as running immediately? No. Zero means as soon as possible, and as soon as possible is after the current run of code finishes and after the promise callbacks already queued by then. It is a way of saying later, not now, which is occasionally useful and much more often a sign that something else should be fixed. ### What is the difference between debounce and throttle? Debounce waits for the activity to stop, so a burst of twenty keystrokes produces one call at the end. Throttle lets at most one call through per interval while the activity continues. Search suggestions and autosave want debounce; scroll and resize handlers want throttle. ### Why is my timer late? Because a timer is a request, not a guarantee. The callback becomes eligible once the delay is up and runs only when the current work finishes, so any long synchronous loop delays it. Background browser tabs also slow timers deliberately, to at most once per second and sometimes much less often or not at all. ### Do you need a library for any of this? No. Debounce and throttle are a few lines each, as this page shows, and request guarding is a comparison and an early return. Libraries add options such as leading and trailing calls or cancellation, which are worth having on a big app and are not worth a dependency on a small one. ## Related - [JavaScript Async](/programming/javascript/async/) for the full topic overview - [Promises and async/await](/programming/javascript/async/promises-and-async-await/) for the promise behavior behind the ordering rules - [Fetch, JSON, and the Network](/programming/javascript/async/fetch-json-and-the-network/) for cancelling a real request with AbortController - [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/) for the live bindings both guards depend on - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## Events, Forms, and User Input in JavaScript URL: /programming/javascript/dom/events-forms-and-user-input/ Cluster: Programming > Javascript A page with a form or a clickable list usually needs the same five jobs: run code when somebody clicks or types, stop a form reloading the page, serve a whole list with one listener, read what was filled in, and keep the submit button honest while a request is in flight. These examples need a browser rather than Node. Save an HTML file, load your script from it with ``, and keep the Console tab open. The `FormData` object examples are the exception, since they also run in Node. ## Listening for Something to Happen `addEventListener` takes the name of an event and a function to run when it happens. The function is a callback, exactly as covered in [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/): ```js const refreshButton = document.querySelector("#refresh"); refreshButton.addEventListener("click", () => { console.log("the button was clicked"); }); ``` Click the button and the message appears in the console. Nothing else happens here because this is a standalone `
  • Book the room
  • ``` ```js document.querySelector("#tasks").addEventListener("click", (event) => { const button = event.target.closest("[data-action='delete']"); if (!button) return; button.closest("li").remove(); }); ``` Click any Delete button and its row disappears. Click anywhere else in the list and nothing happens, because the guard returned early. That `closest` call matters. It walks upward from the clicked element, checking the element itself first, and returns the nearest match or `null` when there is none. Without it a click that landed on an icon inside the button would miss. This pattern is called delegation, and it buys two things. Rows added later are handled with no extra code, since the listener lives on the container. And there is one function to remove when the feature goes away rather than twenty. It works for events that reach the container, which covers `click` and most of what you will delegate. For `focus` and `blur`, which do not bubble, delegate `focusin` and `focusout` instead, or register with the capture option. ## Reading a Form with FormData Reading each field with its own `querySelector` gets long quickly. Passing the form element to `FormData` collects every field at once, and the browser decides which fields count: ```html
    ``` ```js document.querySelector("#signup").addEventListener("submit", async (event) => { event.preventDefault(); const data = new FormData(event.currentTarget); const body = Object.fromEntries(data); await fetch("/api/signup", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); }); ``` The `name` attribute is what makes a field appear. A field without one is left out entirely, and so is a disabled field or an unchecked checkbox, which is the usual reason a value goes missing on the server. The object side behaves in ways worth seeing before they surprise you. This part is not DOM specific, so it also runs in Node: ```js const data = new FormData(); data.append("email", "sam@example.com"); data.append("topic", "billing"); data.append("topic", "shipping"); data.append("quantity", 3); console.log("get:", data.get("topic")); console.log("getAll:", data.getAll("topic")); console.log("missing:", data.get("nope")); console.log("as an object:", Object.fromEntries(data)); console.log("quantity type:", typeof data.get("quantity")); ``` ```text // node output: // get: billing // getAll: [ 'billing', 'shipping' ] // missing: null // as an object: { email: 'sam@example.com', topic: 'shipping' } // quantity type: string ``` Three findings there matter. A missing field gives `null` rather than an error, and `Object.fromEntries` keeps only the last value when a name repeats, so a checkbox group needs `getAll` or a loop over the entries instead. The third is that a number you appended came back as the string `"3"`. Every value is either a string or a file, and anything else you append is converted to a string on the way in. You can also send the `FormData` object straight as a `fetch` body, with no `Content-Type` header of your own, and the browser encodes it and sets the header itself. That is the path to take when the form uploads a file. One trap sits next to this. `form.submit()` does not fire the `submit` event and skips validation, so a script that calls it bypasses your handler entirely. Use `form.requestSubmit()` when you want to submit a form from code and still be treated like a real submission. ## Validation the Browser Already Does Before writing any validation of your own, use the attributes. A `required` field, a `type="email"` input, `minlength`, `min` and `max`, and `pattern` are checked by the browser with no script at all. On an ordinary submission, a failing rule stops the browser's submission algorithm before the `submit` event, and the browser shows its own message. So your handler normally runs only once the built-in rules have passed. Three things opt out of that: `novalidate` on the form, `formnovalidate` on the submit button, and calling `form.submit()` from script, which skips validation and the event together. Two methods let your script work with the same system. `checkValidity` reports whether a field or form passes without showing the browser's message, though it does fire an `invalid` event on each failing control, while `reportValidity` checks and shows the message: ```js const form = document.querySelector("#signup"); const email = document.querySelector("[name='email']"); console.log(email.checkValidity()); form.addEventListener("submit", (event) => { event.preventDefault(); if (!form.reportValidity()) return; console.log("passed validation"); }); ``` For a rule the browser has no idea about, such as two passwords matching, use `setCustomValidity`. Setting a non-empty string marks the field invalid with your wording, and setting an empty string clears that custom error, though the field can still fail on `required`, its type, or another attribute: ```js confirmField.setCustomValidity( confirmField.value === passwordField.value ? "" : "The two passwords do not match", ); ``` Remember what this is and is not. Everything here happens on the person's own machine, where it can be edited or skipped, so it is a courtesy to honest users rather than a guarantee. The server has to check the same things again. ## Disabled and Loading States A submit button that stays clickable during a request invites the double submission, and that is how a person ends up subscribed twice. Disable it before the request and restore it afterwards, whatever happened: ```js form.addEventListener("submit", async (event) => { event.preventDefault(); const button = form.querySelector("button[type='submit']"); const original = button.textContent; button.disabled = true; button.textContent = "Sending..."; try { const response = await fetch("/api/signup", { method: "POST", body: new FormData(form) }); if (!response.ok) throw new Error(`Request failed: ${response.status}`); form.querySelector(".status").textContent = "Thanks, you are on the list."; } catch (error) { form.querySelector(".status").textContent = "Something went wrong. Please try again."; } finally { button.disabled = false; button.textContent = original; } }); ``` The reader sees the button stop responding and read Sending, then come back with either a thank you or an apology, with the exact disabled styling coming from your CSS or the browser's default. The `finally` block is what guarantees the second half, since a thrown error would otherwise leave the button disabled forever. The `response.ok` check is not optional, for the reasons the [fetch guide](/programming/javascript/async/fetch-json-and-the-network/) sets out. A failed request that never throws would show your success message. Two details finish the job. A disabled control is excluded from `FormData`, so disable the button rather than the fields if you are still reading them. And put the status message somewhere the reader is already looking, since a message below the fold is a message nobody sees. ## Pitfalls and Debugging The page navigates when the form is submitted. Likely nothing called `preventDefault`, or it was called after an `await`, or something threw before it, or the listener sits on the button rather than the form. Listen for `submit` on the form itself, which also covers somebody pressing Enter in a field. The handler runs twice. Most often the listener was added more than once, because the setup code runs on every render or every time a panel opens, and the [cleanup guide](/programming/javascript/dom/listener-cleanup-and-page-lifecycle/) covers that case. It can also be one listener seeing two real events, so log the event and its target before assuming. A field is missing on the server. Check for a `name` attribute, then check whether the control is disabled or an unchecked checkbox. All three are excluded from `FormData` by design. Clicks stop working on new rows. Listeners were attached to elements that have since been replaced. Delegate from a container that survives the update. A click on an icon inside a button does nothing. The event started on the icon, so a check such as `event.target.matches("button")` fails. Use `event.target.closest("button")` instead. Nothing happens and there is no error. Log inside the listener first to see whether it runs at all. If it never runs, work through the candidates: the selector, the timing, the event name, a control that cannot be activated, a listener already removed or aborted, or something calling `stopPropagation` further down the path. The Enter key submits when you did not expect it. Forms support implicit submission, and whether it happens depends on the fields present and whether the form has a default submit button. A `button` inside a form is `type="submit"` unless you say otherwise, so write `type="button"` on any button that is not meant to submit. ## Frequently Asked Questions ### Should you use onclick in the HTML? Prefer addEventListener. An onclick attribute allows only one handler, mixes behavior into your markup, and can be blocked by a strict Content Security Policy, which many sites run. It still works otherwise, so you will meet it in older code, but there is nothing it does that addEventListener does not do better. ### Why does my form reload the page? Because a form submission runs the browser's own submission algorithm, which normally navigates, and nothing cancels it unless you do. Call preventDefault inside the submit handler, before it awaits anything. If it still navigates, check that the handler is on the form rather than the button, that it registered at all, and that nothing threw before the call. ### What is the difference between target and currentTarget? The target is where the event was dispatched, such as the button clicked, though it is reported as the host element once the event leaves a shadow tree. The currentTarget is the element whose listener is running now, which for a delegated listener is the container. Delegation works by letting the event reach that container, then inspecting the target. ### Do you need a library for form validation? Not to start. Required fields, email and number types, minimum and maximum lengths, and pattern matching are built into HTML, and reportValidity shows the browser's own messages. Libraries earn their place when you need shared rules between the browser and a server, or messages the browser cannot phrase. ## Related - [JavaScript and the DOM](/programming/javascript/dom/) for the full topic overview - [Selecting Elements and Updating the Page](/programming/javascript/dom/selecting-elements-and-updating-the-page/) for finding and changing the elements these handlers touch - [Listener Cleanup and Page Lifecycle](/programming/javascript/dom/listener-cleanup-and-page-lifecycle/) for taking these listeners back off again - [Fetch, JSON, and the Network](/programming/javascript/async/fetch-json-and-the-network/) for sending what the form collected - [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/) for the callbacks every listener here is built from - [HTML](/programming/html/) for the form controls and attributes this page reads - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## JavaScript and the DOM URL: /programming/javascript/dom/ Cluster: Programming > Javascript This topic covers the JavaScript that changes a web page. Finding an element, putting text into it, reacting when somebody clicks or types, reading what they filled in, and taking your listeners back off again. It is the half of JavaScript people picture when they picture JavaScript. It is also the half a framework hides from you, which is why framework users still land here when something behaves oddly and the framework has no opinion about it. ## JavaScript DOM Topics - [Selecting Elements and Updating the Page](/programming/javascript/dom/selecting-elements-and-updating-the-page/) covers `querySelector` and `querySelectorAll`, the difference between `textContent` and `innerHTML` and why the safe one is the default, adding and removing classes, reading attributes and `data-` values, and building or deleting elements. - [Events, Forms, and User Input](/programming/javascript/dom/events-forms-and-user-input/) covers `addEventListener`, what the event object hands you, cancelling a default behavior with `preventDefault`, one listener that serves a whole list, reading a form with `FormData`, validation the browser already does, and the disabled and loading states a submit button needs. - [Listener Cleanup and Page Lifecycle](/programming/javascript/dom/listener-cleanup-and-page-lifecycle/) covers `removeEventListener` and why it so often does nothing, cancelling a whole set of listeners with one `AbortController`, when your script runs against `defer` and `DOMContentLoaded`, and why these leaks survive a move to React. ## The Rule Everything Here Depends On The page you see is not the HTML file. The browser reads that file once and builds a tree of objects from it, and every line of JavaScript on these pages is reading or changing that tree. Two consequences follow, and they explain most beginner DOM bugs. Your code cannot touch a part of the tree that has not been built yet, which is why a script placed too early finds nothing. And editing the tree is what changes the screen, at the browser's next chance to paint. That is also why a reload discards the changes your script made to the tree. The browser goes back to the file and rebuilds it from scratch, so anything you want to survive has to be saved somewhere, such as storage or a server. ## How the Three Guides Fit Together The first guide is one direction of the conversation: your code talking to the page. Find the thing, change the thing, add a thing, remove a thing. The second guide is the other direction: the page telling your code that a person did something. A click, a keystroke, a form submitted. Almost every real feature is a loop between the two, and the newsletter form on the [JavaScript guide](/programming/javascript/) is exactly that loop. The third guide is the part tutorials skip. A listener you attached stays attached, holding on to the data it refers to, until something takes it off or its target goes away. That is the difference between a demo and a feature that survives a user navigating around. ## Where You Run These Examples These examples need a browser rather than Node. Node has no page, no elements, and no clicks, so a DOM example pasted into a terminal fails on the first line. Save an HTML file with a `` tag in the head, put your JavaScript in `app.js` beside it, and open the file in a browser. Keep the browser tools open on the Console tab while you work, because that is where your errors and your `console.log` output appear. A few things on these pages are not DOM specific and also run in Node, such as `FormData` and the listener machinery itself. Each one says so where it appears. ## Common Pitfalls - Running before the element exists: a plain script tag in the head, with no `defer` and no `type="module"`, runs while the page is still being parsed, so `querySelector` returns `null`. Module scripts are deferred already and do not have this problem. The [lifecycle guide](/programming/javascript/dom/listener-cleanup-and-page-lifecycle/) covers the loading order. - Reaching for `innerHTML` to show text: it parses whatever you give it as markup, so a value from a person or an API can bring behavior with it. The [selecting guide](/programming/javascript/dom/selecting-elements-and-updating-the-page/) covers when the risk is real and what to use instead. - Attaching a listener to every row: a hundred rows means a hundred listeners, and rows added later have none at all. One listener on the container solves both for events that reach it, such as `click`, and the [events guide](/programming/javascript/dom/events-forms-and-user-input/) shows the pattern and its limits. - Letting a form navigate while your code is mid request: native submission is a real option, but once your handler is taking over and doing its own async work, nothing cancels the navigation unless `preventDefault` does. The same guide covers reading the form properly with `FormData`. - Adding listeners without removing them: attaching on every render, or every time a panel opens, quietly stacks duplicates that all fire together. The [cleanup guide](/programming/javascript/dom/listener-cleanup-and-page-lifecycle/) covers both cures. ## Common Questions ### Which guide should you read first? Start with selecting elements and updating the page, because the other two assume you can find an element and change it. Events and forms comes next, since that is where a page starts responding to a person. Listener cleanup comes last, because it is about undoing what the first two set up. ### Do you need any of this if you use React? Yes, though you write less of it by hand, since a framework owns the updating. The event object, preventDefault, form data, and listener cleanup all still show up in framework code, and the DOM concepts behind them carry over unchanged. How a bug looks can differ, because a framework wraps events and runs its own cleanup. ### Is jQuery still worth learning for this? Not as a starting point. The features that made it essential, such as one-line selection and consistent events across browsers, are now built into the browser as querySelector and addEventListener. You will still meet it in older codebases, which is a reason to read it rather than a reason to reach for it first. ### Why is my script saying the element is null? The likeliest cause is timing: a classic script in the head runs during parsing, before the body exists, and defer fixes that, while a module script is deferred already. Check the other causes too, since null also means a wrong selector, a wrong search root, an iframe or shadow tree, or content that is not there yet. ## Continue Learning JavaScript - Return to the [JavaScript guide](/programming/javascript/) for where the language fits, the full learning path, and what to build first. - Read [JavaScript Language Basics](/programming/javascript/language-basics/) for the functions, closures, and objects every example here assumes. - Read [JavaScript Async](/programming/javascript/async/) for the promises and `fetch` calls a form handler makes once it has your data. - Read [HTML](/programming/html/) for the elements, attributes, and form controls this topic reaches into. --- ## Listener Cleanup and Page Lifecycle URL: /programming/javascript/dom/listener-cleanup-and-page-lifecycle/ Cluster: Programming > Javascript Every listener you add is one you may need to remove. That means knowing how to take a listener back off, how to retire a whole feature's listeners with one call, when your script runs against the page being built, and why none of this goes away when you move to a framework. The examples that print output also run in Node, because the listener machinery here is `EventTarget`, the same interface every DOM element inherits from. The parts that need a real page say so. ## Why a Listener Outlives What Added It Adding a listener creates a lasting arrangement. The target keeps a reference to your function until something removes it, and the ways that happens are an explicit removal, the `once` option, an aborted signal, or the target itself going away. That is fine when both live and die together. It goes wrong when the listener sits on something permanent while the thing that cared about it is temporary: ```js function openPreview(item) { window.addEventListener("resize", () => { document.querySelector("#preview").textContent = item.title; }); } ``` Open the preview five times and there are five resize listeners, all live, all firing on every resize. The fifth one is doing the visible work and the other four are doing it again underneath. The cost is not only wasted work. Each of those functions is a closure over `item`, the live binding behavior covered in [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/), so each one retains that data for as long as `window` exists. Whether a person notices depends on how much is retained and how often the event fires. Adding the identical function twice for the same target, event type, and capture setting registers it once, not twice: ```js const target = new EventTarget(); let count = 0; const handler = () => { count += 1; }; target.addEventListener("ping", handler); target.addEventListener("ping", handler); target.dispatchEvent(new Event("ping")); console.log("times the handler ran:", count); ``` ```text // node output: // times the handler ran: 1 ``` That protection is narrower than it looks. It needs the very same function object, and an arrow function written inline creates a brand new one every time the surrounding code runs, so the duplicates in the first example are all distinct. The same function registered once for capture and once for bubble is also two listeners, not one. ## removeEventListener Needs the Same Function The removal method takes the event name and the function, and it removes nothing unless the type, the function, and the capture setting all match what you added. The function is the part people get wrong, and it is the most common reason cleanup code appears to do nothing: ```js const target = new EventTarget(); let count = 0; target.addEventListener("pong", () => { count += 1; }); target.removeEventListener("pong", () => { count += 1; }); target.dispatchEvent(new Event("pong")); console.log("times the handler ran after removal:", count); ``` ```text // node output: // times the handler ran after removal: 1 ``` Two identical-looking arrow functions are two different objects, so the removal matched nothing and the listener stayed. No error was raised, which is what makes it so easy to miss. The fix is to give the function a name and keep hold of it. Browser-side, that looks like this: ```js function handleResize() { console.log("resized"); } window.addEventListener("resize", handleResize); window.removeEventListener("resize", handleResize); ``` The same rule applies to a bound function, since `.bind()` returns a new function every time it is called. Store the bound version in a variable and pass that variable to both calls. If you registered with `capture: true`, pass it again when removing; `once`, `passive`, and `signal` play no part in the match. ## One AbortController for Every Listener Keeping a named reference for each of ten listeners is tedious and easy to get half right. Pass one controller's signal to all of them instead, and a single call retires the lot: ```js const target = new EventTarget(); const controller = new AbortController(); let total = 0; target.addEventListener("a", () => { total += 1; }, { signal: controller.signal }); target.addEventListener("b", () => { total += 10; }, { signal: controller.signal }); target.dispatchEvent(new Event("a")); target.dispatchEvent(new Event("b")); controller.abort(); target.dispatchEvent(new Event("a")); target.dispatchEvent(new Event("b")); console.log("total after aborting:", total); ``` ```text // node output: // total after aborting: 11 ``` Both listeners fired once, the abort removed both, and the second pair of dispatches changed nothing. No named function had to be kept for either one. The controller is safe to use after aborting, in the sense that it will not throw. A listener added with a signal that has already been aborted is simply never registered: ```js let late = 0; target.addEventListener("c", () => { late += 1; }, { signal: controller.signal }); target.dispatchEvent(new Event("c")); console.log("listener added after abort ran:", late); ``` ```text // node output: // listener added after abort ran: 0 ``` That is a trap in a feature that reopens. A controller is spent once aborted, so create a fresh one each time the feature starts rather than reusing the old one. This is the same `AbortController` that cancels a request in the [fetch guide](/programming/javascript/async/fetch-json-and-the-network/). One controller can therefore own a feature's listeners and its in-flight requests together, and one `abort` call closes the whole thing down. ## The Listener That Should Only Fire Once When a listener's whole job is to run one time, the `once` option takes it off for you. The removal happens before your function is called, which matters if that function dispatches the same event again: ```js const target = new EventTarget(); let count = 0; target.addEventListener("solo", () => { count += 1; }, { once: true }); target.dispatchEvent(new Event("solo")); target.dispatchEvent(new Event("solo")); console.log("times the handler ran:", count); ``` ```text // node output: // times the handler ran: 1 ``` The before, not after, matters when the handler can cause the same event again. Here it dispatches recursively and still runs exactly once, because it was already off the target by the time it started: ```js const target = new EventTarget(); let calls = 0; target.addEventListener("boom", function handler() { calls += 1; if (calls < 5) target.dispatchEvent(new Event("boom")); }, { once: true }); target.dispatchEvent(new Event("boom")); console.log("times the handler ran:", calls); ``` ```text // node output: // times the handler ran: 1 ``` Reach for it on things that happen once, such as dismissing a first-run banner or reacting to the first interaction that unlocks audio playback. A dialog closing is not one of them, since a dialog can reopen and close again. It is cleanup you cannot forget to write. ## When Your Script Actually Runs Timing is the first thing to rule out when a selector you believe in returns `null`. A classic script tag with no `defer` runs the moment the parser reaches it, and elements further down the page do not exist yet. It is not the only cause. A correctly spelled selector can also match nothing yet, or be run against the wrong root: another document, an iframe, or a shadow tree. The `defer` attribute is the fix worth defaulting to. The browser downloads the file while it carries on parsing, then runs deferred scripts in source order once parsing is finished: ```html ``` A script with `type="module"` behaves this way already and needs no `defer` attribute. That holds for an inline module too. It has no file to fetch, but it is still evaluated as a module and still runs after parsing rather than where it sits. An inline classic script is the one that always runs where it sits. `defer` has no effect on it, because the attribute only governs a script the browser has to fetch. Its opposite, `async`, runs the file as soon as it has downloaded, interrupting parsing, in whatever order the downloads finish. That suits an independent script such as analytics and does not suit code that touches your elements. Two page events complete the picture. `DOMContentLoaded` fires once the HTML has been parsed and the deferred and module scripts have run, and it does not wait for images. It does not wait on stylesheets directly either, though deferred scripts do, and it waits for them: ```js document.addEventListener("DOMContentLoaded", () => { document.querySelector("#app").textContent = "Ready"; }); ``` The `load` event on `window` waits for the page's dependent resources, styles, scripts, frames, and images that are not lazily loaded, so it fires noticeably later. Use it only when you need a resource to have finished, such as measuring an image. With `defer` in place you rarely need `DOMContentLoaded` at all, since your code already runs after parsing. It stays useful for a script you do not control the loading of, and there the event alone is not enough. An async or dynamically inserted script can start after the event has already fired, and a listener added then never runs. Check the state before deciding, so the code works either way: ```js function start() { document.querySelector("#app").textContent = "Ready"; } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", start, { once: true }); } else { start(); } ``` ## Why These Leaks Survive a Framework Moving to React or Vue does not retire this page. Event systems differ between frameworks, and a hand-added listener on a node the framework removes can be collected with that node, so the danger is narrower than the folklore. The case that bites is a listener on a long-lived target such as `window` or `document`, added from inside a component. Nothing the framework unmounts is that target, so only your own teardown removes it: ```js useEffect(() => { const controller = new AbortController(); window.addEventListener("resize", handleResize, { signal: controller.signal }); document.addEventListener("keydown", handleEscape, { signal: controller.signal }); return () => controller.abort(); }, []); ``` That returned function is React's teardown, and it runs on unmount and before the effect runs again. The controller pattern means both listeners come off in one line, and a forgotten one is much harder to arrange. React's Strict Mode makes this visible in development by running each effect through an extra setup, cleanup, setup cycle. Missing cleanup shows up there, though not always as a doubled call, since re-registering the identical function for the same type and capture is deduplicated. Read it as React pointing at your teardown rather than as a bug in React. The same reasoning applies outside React. Code that runs again, whether on a route change, a reopened panel, or a re-rendered list, needs to undo whatever it set up that accumulates or persists. Work that simply overwrites a value each time needs no teardown. ## Pitfalls and Debugging The handler runs twice, then three times. The likely cause is setup code running repeatedly, each run adding a new function. Confirm it before fixing it, since two real dispatches produce the same count, then move to a controller you abort on the way out, or keep a named function and remove it. removeEventListener changed nothing. The function passed to it was not the function that was added, usually because both were written inline. Store it in a variable and pass that variable both times. The selector is right and the element is null. Most often a classic script ran before the element was parsed, which `defer` fixes and which a module script does not have. Check the search root too, since a query against the wrong document, an iframe, or a shadow tree gives the same answer. The feature works once and never again. An `AbortController` is being reused after it was aborted, so the second round of listeners is never registered. Create a new controller each time the feature starts. The page gets slower the longer it is open. Look for listeners on `window`, `document`, or `body` added by code that runs more than once. Those targets outlive almost every feature attached to them, even though a navigation does eventually retire the document. An interval is still running after the panel closed. Timers are not listeners and a controller does not touch them. Call `clearInterval` in the same teardown, as the [timers guide](/programming/javascript/async/timers-ordering-and-race-conditions/) covers. ## Frequently Asked Questions ### Do listeners not get cleaned up automatically? Sometimes. When an element is removed and nothing else refers to it, the element and its listeners can be collected together. The leaks happen when the listener is on something long-lived such as window or document, because that target keeps the function alive, and the function retains the values it refers to. ### Is AbortController only for cancelling fetch? No. The same controller cancels listeners too. Pass its signal in the options object of addEventListener, and one abort call removes every listener registered with it. It is the same object either way, so a single controller can retire a feature's requests and its listeners together. ### Is defer better than putting the script at the end of the body? Both work, and defer is the one to reach for. It states the intent in the tag: download while parsing continues, then run in source order once parsing finishes. A body-end script relies on position for the same effect, and browsers may still fetch it early through speculative scanning. ### How do you tell whether you have a leak? Watch for the symptom first: a handler that fires more often each time you reopen a feature. In Chrome DevTools, the Elements panel has an Event Listeners tab for the selected element, and the Memory panel compares heap snapshots taken before and after using the feature. Other browsers offer equivalent views under their own names. ## Related - [JavaScript and the DOM](/programming/javascript/dom/) for the full topic overview - [Events, Forms, and User Input](/programming/javascript/dom/events-forms-and-user-input/) for the listeners this page takes back off - [Selecting Elements and Updating the Page](/programming/javascript/dom/selecting-elements-and-updating-the-page/) for the selectors that return null when the timing is wrong - [Fetch, JSON, and the Network](/programming/javascript/async/fetch-json-and-the-network/) for the same AbortController cancelling a request - [Timers, Ordering, and Race Conditions](/programming/javascript/async/timers-ordering-and-race-conditions/) for clearing intervals in the same teardown - [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/) for why a listener holds on to the data it can see - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## Selecting Elements and Updating the Page URL: /programming/javascript/dom/selecting-elements-and-updating-the-page/ Cluster: Programming > Javascript A typical page script finds elements, changes their text safely, switches classes, reads the data attached to them, and builds or deletes elements from JavaScript. Everything here needs a browser rather than Node, since Node has no page to select from. Save an HTML file, load your JavaScript from it with ``, and open it in a browser with the Console tab showing. Most examples describe what you would see, because the result of this work is a changed page. ## Finding an Element One method does most of the work. `document.querySelector` takes a CSS selector, the same syntax you already write in a stylesheet, and hands back the first element that matches: ```html

    Weekly Report

    Not started

    ``` ```js const title = document.querySelector("#title"); const status = document.querySelector(".status"); const refreshButton = document.querySelector("[data-action='refresh']"); ``` A hash finds an id, a dot finds a class, a bare word finds a tag, and square brackets find an attribute. Valid CSS selectors work here, including descendant selectors such as `.card h2`, though an invalid string throws and a pseudo-element selector matches nothing. The search also has edges worth knowing. It stays inside the document or element you called it on, so it never reaches into an iframe or a shadow root, and an id or class generated at runtime may need `CSS.escape()` before it is safe to paste into a selector. When nothing matches you get `null`, not an error. That is worth knowing early, because the error arrives one line later and blames the wrong thing: ```js const missing = document.querySelector("#does-not-exist"); console.log(missing); missing.textContent = "hello"; ``` ```text // browser console, illustrative wording: // null // TypeError: Cannot set properties of null (setting 'textContent') ``` Read that message as a selector problem rather than a text problem. The usual causes are a wrong selector, a script that ran before the element existed, or a search started from the wrong root, such as a different document or an iframe. The timing case is the one people meet first, and the [lifecycle guide](/programming/javascript/dom/listener-cleanup-and-page-lifecycle/) covers it. Older code uses `getElementById`, `getElementsByClassName`, and `getElementsByTagName`. They still work, and you will meet them, but one method with CSS selectors is less to remember. `getElementById` is the odd one out, because it belongs to the document rather than to elements. Calling it on an element throws, and there would be no point anyway, since it searches the whole document and ids are meant to be unique in it. ## Finding Many Elements `querySelectorAll` returns every match rather than the first. What comes back is a `NodeList`, which looks like an array and is not one: ```js const rows = document.querySelectorAll(".row"); console.log(rows.length); rows.forEach((row) => row.classList.add("loaded")); const titles = Array.from(rows).map((row) => row.textContent); ``` A `NodeList` has `length`, index access, and `forEach`. It does not have `map`, `filter`, or `reduce`, so `Array.from` or a spread into `[...rows]` is the usual first move when you want those. There is one more difference worth carrying. A `querySelectorAll` result is static. It is a snapshot taken at the moment you asked, and elements added afterwards are not in it. The older `getElementsByClassName` returns a live `HTMLCollection` instead, which does update itself as the page changes. Removing items while looping forward over a live collection skips entries, because the indexes shift underneath you. You can also search inside one element rather than the whole document. Every element has `querySelector` and `querySelectorAll`, and narrowing the search root is the reliable way to stop a selector matching something you did not mean: ```js const card = document.querySelector("#report-card"); const cardLinks = card.querySelectorAll("a"); ``` ## Putting Text on the Page Setting `textContent` replaces everything inside an element with a single piece of text. Whatever you assign is treated as text, so angle brackets and quotes arrive on screen exactly as typed: ```js const status = document.querySelector(".status"); status.textContent = "Ready"; status.textContent = "5 < 10 is true"; ``` The second line shows the useful part. The reader sees the literal characters `5 < 10 is true`, because nothing in that string is treated as markup. Two neighbors come up in older code. Read from a rendered element, `innerText` is aware of styling, so it skips hidden elements and returns roughly what a person can see, at the cost of possibly forcing the browser to work out the layout first. Read from an element that is not rendered, or one you built and have not attached yet, it falls back to something close to `textContent`. That inconsistency is a second reason not to reach for it. `textContent` ignores styling and returns the text of everything inside, including `script` and `style` elements. It is the simpler and cheaper of the two, and it is the right default for setting text. ## When You Actually Need innerHTML Setting `innerHTML` hands the browser a string and asks it to parse the string as markup. That is useful when you are inserting a structure you wrote yourself: ```js const card = document.querySelector("#report-card"); card.innerHTML = "

    Weekly Report

    Nothing due today.

    "; ``` The risk starts when any part of that string came from somewhere you do not control: a form field, an API response, a URL, a database row somebody else can write to. The browser cannot tell your markup from theirs. A `script` element inserted this way does not run, and that holds for module scripts too, which is the detail people remember and then over-trust. Attribute-based handlers can still run, and that is enough: ```js const nameFromUser = ""; card.innerHTML = nameFromUser; ``` The image request fails, its error handler fires, and whatever sat in that attribute has just executed on your page with your user's session. Two things have to hold for it: the source has to fail to load, and no Content Security Policy on the page may be blocking inline handler attributes. MDN uses this exact shape as its worked example of why the property is the most common route into a cross-site scripting bug. Treat the CSP as a second line of defense rather than the reason you are safe. Three rules keep this simple. Use `textContent` whenever the value is text, which is most of the time, and build structure with `createElement` while setting the text separately. If you must insert markup that came from elsewhere, run it through a maintained sanitizer such as DOMPurify first, rather than filtering it yourself. The `+=` shortcut deserves its own warning. Writing `card.innerHTML += "

    more

    "` re-parses the entire contents, which throws away and rebuilds every element inside. Listeners registered on those descendants go with them, along with live state such as a partly typed input, though a listener on `card` itself survives. ## Adding and Removing Classes Styling from JavaScript is best done by switching classes rather than by writing styles directly. The CSS stays in the stylesheet and the JavaScript only decides which state applies: ```js const panel = document.querySelector("#filters"); panel.classList.add("is-open"); panel.classList.remove("is-loading"); panel.classList.toggle("is-open"); console.log(panel.classList.contains("is-open")); ``` All four methods accept a class name without a leading dot, and `add` and `remove` take several names at once. `toggle` returns `true` when the class ended up present and `false` when it ended up absent. `toggle` also takes a second argument that decides the outcome instead of flipping it. This is the version to reach for when a condition already tells you the answer: ```js panel.classList.toggle("is-open", itemCount > 0); ``` Avoid assigning `element.className` directly. It replaces the whole class attribute, so a single assignment silently drops every other class the element was carrying. ## Attributes and Data Values Attributes are what you wrote in the HTML, and `getAttribute` and `setAttribute` read and write them by name: ```js const link = document.querySelector("#docs-link"); console.log(link.getAttribute("href")); link.setAttribute("href", "/programming/javascript/"); link.removeAttribute("target"); ``` Many attributes also exist as properties on the element, and the property is usually the nicer one to use. `link.href`, `input.value`, and `checkbox.checked` all read and write live state. The two are not always the same thing. An input's `value` property is what the person has typed right now, while its `value` attribute is the starting value from the HTML, and after typing they disagree. For your own information, use `data-` attributes rather than inventing attribute names. The browser gathers them into a `dataset` object, dropping each dash that comes before a lowercase letter and capitalizing that letter: ```html ``` ```js const button = document.querySelector("button"); console.log(button.dataset.action); console.log(button.dataset.itemId); button.dataset.itemId = "43"; ``` So `data-item-id` in the HTML becomes `dataset.itemId` in JavaScript. Write the attribute with dashes and lowercase letters, because a capital letter in the HTML is lowercased before the conversion and will not produce the property you expected. Every value in `dataset` is a string. An id read back as `"42"` needs `Number(...)` before you do arithmetic with it, which is the same coercion trap covered in [Values, Variables, and Equality](/programming/javascript/language-basics/values-variables-and-equality/). ## Creating and Removing Elements Building an element takes three steps: create it, fill it, then attach it. Nothing appears on screen until the third step, because an element that is not in the tree is not on the page: ```js const list = document.querySelector("#tasks"); const item = document.createElement("li"); item.textContent = "Send the weekly report"; item.classList.add("task"); item.dataset.itemId = "42"; list.append(item); ``` The reader sees a new list item appear at the end of the list, styled by whatever your stylesheet says about `.task`. Nothing about the HTML file on the server changed. `append` is the modern method and the more flexible one: it takes several nodes at once, and it accepts plain strings, which it inserts as text. The older `appendChild` takes exactly one node and rejects strings. Adding many elements one at a time touches the live page once per element. A `DocumentFragment` is a holding area that is not part of the page, so the whole batch reaches the document in a single insertion: ```js const fragment = document.createDocumentFragment(); for (const task of tasks) { const item = document.createElement("li"); item.textContent = task.title; fragment.append(item); } list.append(fragment); ``` Removing is simpler than it used to be. Call `remove` on the element itself, with no reference to its parent needed: ```js document.querySelector("#tasks .task").remove(); list.replaceChildren(); ``` The second line empties the list completely. `replaceChildren` with no arguments is the readable way to clear an element, and it replaces the old habit of assigning an empty string to `innerHTML`. ## Pitfalls and Debugging Cannot read properties of null. The selector matched nothing at that moment. Print the result of `querySelector` on its own line first, then check the spelling, the search root, and whether your script runs before the element is parsed. A class change did nothing visible. The class is on the element but no CSS rule targets it. Select the element in Chrome DevTools and look at the Styles pane, which shows the rules that matched and the ones that were overridden; other browsers have the same view under their own names. Every other class vanished. Something assigned `className` instead of calling `classList.add`. The whole attribute was replaced. Text arrived with visible tags in it. Content that was meant as markup went through `textContent`, which is the safe direction of that mistake. Use `createElement` to build the structure, and keep the text going through `textContent`. Listeners stopped working after an update. Rewriting `innerHTML` destroys the elements inside and builds new ones, and the new ones have no listeners. The [events guide](/programming/javascript/dom/events-forms-and-user-input/) shows the delegation pattern that survives this. The loop skipped items. A live `HTMLCollection` from `getElementsByClassName` shrinks as you remove elements from it, so a forward loop moves past entries it never visited. Take a static snapshot with `querySelectorAll`, or convert to an array first. Nothing appeared even though there was no error. The element was created and filled but never attached with `append`. Log the element and check whether it has a parent. ## Frequently Asked Questions ### Should you still use getElementById? It works and it is not deprecated, so existing code using it is fine. For new code querySelector covers the same ground, since it takes any CSS selector, including an id written with a hash. It is also the one you can call on an element, because getElementById belongs to the document and searches all of it. ### Why does map not work on querySelectorAll? Because a NodeList is not an array. It has length, index access, and forEach, but not map, filter, or reduce. Wrap it in Array.from, or spread it into an array, and every array method becomes available on the result. ### Is innerHTML always unsafe? No. The risk is assigning a string you did not fully control, such as a value from a person, an API, or a URL, because the browser parses it as markup and attribute-based handlers inside it can run. A fixed string you wrote yourself carries no such risk. Use textContent whenever the value is text. ### Why did my styles stop applying after a class change? Usually because className was assigned rather than added to, which replaces every class the element had. Use classList.add and classList.remove to change one class and leave the others alone. Check the element in the browser tools to see which classes it carries. ## Related - [JavaScript and the DOM](/programming/javascript/dom/) for the full topic overview - [Events, Forms, and User Input](/programming/javascript/dom/events-forms-and-user-input/) for reacting when somebody clicks or types - [Listener Cleanup and Page Lifecycle](/programming/javascript/dom/listener-cleanup-and-page-lifecycle/) for when your script runs and how to undo what it did - [Objects, Arrays, and Destructuring](/programming/javascript/language-basics/objects-arrays-and-destructuring/) for the array methods a NodeList does not have - [Values, Variables, and Equality](/programming/javascript/language-basics/values-variables-and-equality/) for why every dataset value arrives as a string - [HTML](/programming/html/) for the elements and attributes these selectors reach into - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## Functions, Callbacks, and Closures in JavaScript URL: /programming/javascript/language-basics/functions-callbacks-and-closures/ Cluster: Programming > Javascript A JavaScript function is best pictured as a packed lunchbox. It holds instructions you can carry somewhere and open later, and it quietly packs a key to the cupboard it came from. That key is what lets a function you defined in one place still reach the right variables when something else opens it three seconds later. ## Declarations Against Expressions A declaration starts with the word `function`. A function expression is a function written where a value is expected, which often means assigning it to a variable but can equally mean passing it straight to something else. The difference that matters first is when each becomes usable: ```js console.log(double(4)); function double(n) { return n * 2; } triple(4); const triple = (n) => n * 3; ``` ```text // node output: // 8 // ReferenceError: Cannot access 'triple' before initialization ``` The declaration worked before its own line, because declarations are hoisted. The engine registers the whole function when it first reads the file. The `const` is hoisted too, but stays unusable until execution reaches its line, which is the error you see above. In practice this rarely decides anything, because most code calls functions after defining them anyway. The reason teams reach for arrow functions is in the next section. Arrow syntax has two forms, and reading other people's code means recognizing both. With a block body in braces you write `return` yourself; with a concise body you leave it out and the expression is returned: ```js const triple = (n) => { return n * 3; }; const tripleShort = (n) => n * 3; console.log(triple(4), tripleShort(4)); ``` ```text // node output: // 12 12 ``` Both are the same function written two ways. The concise form is why array callbacks are usually one line, and the block form is what you want as soon as the body needs a variable or an early return. An arrow is not only a shorter spelling. It has no `this` of its own, no `arguments` object, cannot be called with `new`, and cannot be a generator. The next section is about the first of those, which is the one you meet daily. ## Arrow Functions and `this` In an ordinary function, `this` is decided by the call form: the object before the dot, whatever `call`, `apply`, or `bind` supplies, the new object under `new`, or nothing at all for a plain call. An arrow function has no `this` of its own and borrows the one from the code around it: ```js const cart = { items: ["lamp", "desk"], countNormal() { return this.items.length; }, countArrow: () => { return typeof this; }, }; console.log(cart.countNormal()); console.log(cart.countArrow()); ``` ```text // node output, running this file as an ES module: // 2 // undefined ``` The normal method saw `cart` because `cart` appeared before the dot at the call site. The arrow function looked outward instead and found the module's top-level `this`, which is `undefined` in an ES module and something else again in a CommonJS file or a classic browser script. The point is that the call site never decides it. An arrow can still end up with the object, as long as it inherited it lexically. An arrow created inside a normal method, or written as a class field, captures the instance and keeps it. Use an arrow when the surrounding `this` is the one you want, or when `this` is irrelevant. Use a normal method when the receiver at the call site is the whole point. The same rule explains a common bug in the other direction. A normal method handed over as a bare callback loses its receiver whenever the API calls it plainly, which most do, though some accept a `thisArg` or invoke it as a method instead: ```js const size = cart.countNormal; size(); ``` ```text // node output: // TypeError: Cannot read properties of undefined (reading 'items') ``` Nothing appeared before a dot when `size()` ran, so `this` was `undefined` and reading `.items` from it failed. Pass `() => cart.countNormal()` instead, or attach the object permanently with `cart.countNormal.bind(cart)`. ## Default and Rest Parameters A default value fills in for a parameter that was not passed, and a rest parameter collects everything left over into an array: ```js function makeTag(name, attributes = {}, ...children) { return [name, JSON.stringify(attributes), children.length]; } console.log(makeTag("p")); console.log(makeTag("ul", { id: "list" }, "a", "b", "c")); ``` ```text // node output: // [ 'p', '{}', 0 ] // [ 'ul', '{"id":"list"}', 3 ] ``` The default applies when the argument is missing or explicitly `undefined`, and not when it is `null`, `0`, or an empty string. That is narrower than `??` from the [values and equality guide](/programming/javascript/language-basics/values-variables-and-equality/), which also falls back on `null`, and narrower still than the looser `||` rule people often expect. Unlike some languages, a JavaScript default is evaluated at every call, so `function log(entries = [])` gets a fresh array each time rather than one shared array that accumulates. The rest parameter must be last, and there can only be one. ## Callbacks A callback is a function handed to something else for that code to call. This is not a special language feature, it is what happens when you pass a function as an argument, which JavaScript allows because functions are ordinary values: ```js const prices = [12, 5, 30]; const doubled = prices.map((price) => price * 2); console.log(doubled); ``` ```text // node output: // [ 24, 10, 60 ] ``` The lunchbox picture holds here. You pack the instructions, hand the box to `map`, and `map` opens it once per element. You never call the arrow function yourself, which is why passing `fn()` instead of `fn` is such a common mistake: the parentheses open the box immediately and hand over whatever fell out. The same shape drives event listeners, timers, array methods, and the promise callbacks covered elsewhere. The timing varies: `map`, `filter`, and `forEach` call yours immediately, during their own call, while a timer or a listener calls it much later. The shape is identical either way. The part that catches people is that the caller decides the arguments, not you. `map` passes three of them to every callback, and only the first is the element: ```js console.log(["1", "5", "10"].map(Number)); console.log(["1", "5", "10"].map(parseInt)); ``` ```text // node output: // [ 1, 5, 10 ] // [ 1, NaN, 2 ] ``` Both lines look like they convert three strings to numbers. The second one received the index as a second argument, and `parseInt` reads its second argument as a number base, so it parsed `"5"` in base 1 and `"10"` in base 2. `Number` receives the extra arguments too and ignores them, which is the only reason the first line works. The lesson generalizes past this one example. Before passing an existing function straight through as a callback, check what extra arguments the caller supplies, and wrap it in `(value) => existing(value)` when you only want the first one. ## Closures A closure is the key packed inside the lunchbox. A function keeps a live link to the variables that were in scope where it was written, not a copy of their values, so it reads whatever they hold at the moment it runs and can update them too: ```js function makeCounter() { let count = 0; return () => { count += 1; return count; }; } const next = makeCounter(); console.log(next(), next(), next()); const other = makeCounter(); console.log(other()); ``` ```text // node output: // 1 2 3 // 1 ``` The variable `count` lives inside `makeCounter`, which returned long before the first call to `next`. The returned function kept it alive, and kept it private. Nothing outside can read or reset `count` except through the function. Because the link is live rather than a copy, each call sees the value the previous call left behind. The second half shows the other half of the rule. Each call to `makeCounter` creates a fresh `count`, so `other` got its own. Closures capture per creation, not per function definition. This is also where the old `var` loop bug comes from, and why it disappeared: ```js const oldStyle = []; for (var i = 0; i < 3; i++) oldStyle.push(() => i); console.log(oldStyle.map((fn) => fn())); const newStyle = []; for (let j = 0; j < 3; j++) newStyle.push(() => j); console.log(newStyle.map((fn) => fn())); ``` ```text // node output: // [ 3, 3, 3 ] // [ 0, 1, 2 ] ``` A `var` gets one binding for the whole enclosing function, or for the whole script or module when there is no function, as here. All three closures shared that one `i` and read its final value. With `let` each iteration gets its own binding, so each closure captured a different one. That difference is the single strongest argument for never writing `var` in new code. ## Higher-Order Functions A higher-order function is one that takes a function, returns a function, or both. You already used one: `map` takes a function. The returning kind is how you build behavior that wraps other behavior: ```js function withLogging(fn) { return (...args) => { const result = fn(...args); console.log("called with", args, "got", result); return result; }; } const loudDouble = withLogging((n) => n * 2); loudDouble(7); ``` ```text // node output: // called with [ 7 ] got 14 ``` The wrapper closes over `fn`, accepts any arguments through a rest parameter, and passes them straight through with spread. It forwards the arguments and the return value, and nothing else: being an arrow, it drops the receiver, cannot be called with `new`, and does not carry the original's own properties. That pattern is everywhere once you recognize it. Debounce wrappers, retry wrappers, permission checks, and React hooks that return handlers are all the same idea: a function built at runtime that remembers its configuration through a closure. ## Pitfalls and Debugging TypeError: Cannot read properties of undefined (reading '...') inside a method. The method was detached from its object, almost always by passing it as a bare callback. Look for the point where the function was handed over without its dot, and wrap it in an arrow function that calls it properly. Calling the callback instead of passing it. `setTimeout(save(), 1000)` runs `save` immediately and then hands its return value over as the handler. Node rejects that with `TypeError: The "callback" argument must be of type function`, while a browser accepts the `undefined` silently, so the only symptom there is `save` running too early. The fix is `setTimeout(save, 1000)`, or `setTimeout(() => save(draft), 1000)` when arguments are involved. The stale closure. A closure holds the binding itself rather than a copy of its value, so it does see later assignments to that same binding, which is exactly why the counter above keeps counting. Staleness comes from a second binding, not from a frozen value. Each React render, or each call to a factory like the counter, creates fresh bindings, so a handler registered during an earlier render keeps reading that earlier render's variables. Copying a value into another variable does the same thing on a smaller scale. Losing the return value of an arrow function. `(n) => n * 2` returns the result, and ``{`(n) => { n * 2 }`}`` returns `undefined`, because braces start a body rather than an expression. Returning an object literal needs parentheses: ``{`(id) => ({ id })`}``. Assuming the arrow form fixes every `this` problem. Written as an object method or a prototype method, an arrow function permanently borrows the wrong scope and the failure is silent rather than loud. Use a normal method there, and save arrows for the callbacks inside it. ## Frequently Asked Questions ### Should you use arrow functions everywhere? Use one whenever the surrounding this is the value you want, or when this is irrelevant, which covers most callbacks. Use a normal method when the receiver at the call site matters. Some callback APIs deliberately supply a this, and a class-field arrow is a legitimate instance-bound method. ### Why is this undefined inside my method? The method was probably detached from its object, usually by passing it directly as a callback. A normal function decides this at call time from what appears before the dot, and there is no dot left. Wrap the call in an arrow function, or use bind. ### Are closures a memory leak? Not by themselves. A closure keeps its captured variables alive only while the function itself is reachable, and both become eligible for collection once nothing refers to them, though the engine chooses when. Leaks come from holding the function forever, such as an event listener that is never removed. ### What's the difference between a callback and a closure? A callback is a role: a function passed to something else for that code to call, sometimes immediately as with map, sometimes much later as with a timer. A closure is a capability: a function that can still reach the variables where it was defined. Most callbacks are also closures. ## Related - [JavaScript Language Basics](/programming/javascript/language-basics/) for the full topic overview - [Values, Variables, and Equality](/programming/javascript/language-basics/values-variables-and-equality/) for the `let` and `const` rules closures depend on - [Objects, Arrays, and Destructuring](/programming/javascript/language-basics/objects-arrays-and-destructuring/) for the array methods that take callbacks - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## JavaScript Language Basics URL: /programming/javascript/language-basics/ Cluster: Programming > Javascript This topic covers the JavaScript that every other page takes for granted. Values and how they compare, functions and the state they capture, the objects and arrays you move data around in, and the module system that lets a project grow past one file. Most of this is the language itself, and it behaves the same way inside a React component, inside a build script, and inside a snippet an AI assistant wrote for you at two in the morning. Where a behavior depends on the host, such as module resolution or top-level `this`, the guides name the host. ## JavaScript Language Basics Topics - [Values, Variables, and Equality](/programming/javascript/language-basics/values-variables-and-equality/) covers `let` and `const`, the primitive types, coercion, the two equality operators, truthiness, and the `??` and `?.` operators that clean up missing data. - [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/) covers the ways to declare a function, what `this` binds to in each, default and rest parameters, and the closures that make callbacks remember anything at all. - [Objects, Arrays, and Destructuring](/programming/javascript/language-basics/objects-arrays-and-destructuring/) covers property access, the array methods worth memorizing, destructuring, spread and rest, `Map` and `Set`, and the copy-against-mutate decision behind a whole family of bugs. - [Modules, Imports, and Exports](/programming/javascript/language-basics/modules-imports-and-exports/) covers named and default exports, import paths that resolve, the ES modules and CommonJS split, and the load order that catches people out in the browser. ## The Rule Everything Here Depends On One fact sits under all four guides. In JavaScript, types belong to values, not to variables, and no type is checked until the code runs. The engine does parse the whole file first and rejects syntax errors before anything executes, but it never objects to a variable holding a different type than last time. ```js let input = "42"; console.log(typeof input); input = 42; console.log(typeof input); input = { id: 42 }; console.log(typeof input); ``` ```text // node output: // string // number // object ``` The same variable held three different types and the engine never complained. That is the trade JavaScript makes. You write a script without declaring anything up front, and in return nothing warns you when a number arrives where a string was expected. Almost every confusing behavior in this topic follows from that one decision. Coercion exists because an operator has to do something when the types do not match. Truthiness exists because `if` has to answer yes or no about any value at all. Optional chaining exists because of what happens next. A missing property already reads as `undefined` on its own, and `?.` stops the access after it from throwing when the value on its left is `null` or `undefined`. ## How the Four Guides Fit Together The first guide gives you the vocabulary. Values, the two equality operators, and what an absent value looks like. Read it first, because the other three write comparisons and default values constantly without pausing to justify them. The second and third guides are about structure. Functions package behavior and quietly capture the variables around them, and objects and arrays package data. Between them they account for most of the lines in any real JavaScript file, framework or not. The fourth guide is about scale. Once a file has more than a few functions worth keeping, modules let you split it and state plainly what each file offers the rest of the project. It comes last because there is no point splitting code you have not written yet. Two subjects deliberately sit outside this topic. Asynchronous work, meaning promises, `await`, and the ordering rules behind them, gets its own topic. Anything that touches a web page, such as selecting elements and handling events, belongs to the DOM rather than to the language. ## Where to Start Depends on Where You Came From Readers reach this topic from three very different places, and the useful entry point is different for each of them. Coming from another language, such as Python, Java, or C#, skim [Values, Variables, and Equality](/programming/javascript/language-basics/values-variables-and-equality/) for the coercion and truthiness rules, then spend your time in [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/). The syntax will look familiar long before the semantics do. Coming from a framework tutorial, where you already ship React or Vue without a plain JavaScript grounding, start with the functions guide and then [Objects, Arrays, and Destructuring](/programming/javascript/language-basics/objects-arrays-and-destructuring/). Between them they explain the props, callbacks, and spreads you have been copying. Starting from nothing, take the four guides in the order they are listed above and type the snippets out rather than reading them. [Modules, Imports, and Exports](/programming/javascript/language-basics/modules-imports-and-exports/) lands better once you have written a file long enough to want splitting. ## Common Pitfalls - Reading `==` as ordinary equality: when the two sides are different types it may convert one before comparing, which is why `0 == ""` is `true`. The [values and equality guide](/programming/javascript/language-basics/values-variables-and-equality/) covers when the loose form is defensible. - Assuming `const` freezes an object: it locks the binding, not the contents, so the properties of a `const` object stay editable. The same guide shows what that means in practice. - Expecting an arrow function to have its own `this`: it borrows the surrounding one, so it fits wherever that lexical value is the one you want. The [functions guide](/programming/javascript/language-basics/functions-callbacks-and-closures/) draws the line. - Copying an object with spread and then editing a nested field: the copy is one level deep, so the nested object is still shared. The [objects and arrays guide](/programming/javascript/language-basics/objects-arrays-and-destructuring/) shows the fix. - Leaving the file extension off an import: native Node ESM requires the extension on a relative path, and a browser needs a URL the server serves. The [modules guide](/programming/javascript/language-basics/modules-imports-and-exports/) covers what resolves and what does not. ## Common Questions ### How much of this is the language and how much is the browser? Most core semantics covered by the first three guides behave the same in Node and browsers. The modules guide crosses the boundary because import resolution and loading rules belong to the host. So do the document object and fetch, and where a guide depends on one of those, it names the host first. ### Do you need to learn all of this before a framework? Not all of it, but the first three guides pay for themselves quickly. React, Vue, Svelte, and Astro all hand you callbacks, pass objects as props, and rely on closures capturing state. Framework code stops looking like ceremony once those three are familiar. ### Does any of this change in TypeScript? The language behavior does not. Coercion, truthiness, and closures work identically, because the JavaScript TypeScript emits follows the same runtime rules. Module resolution is the exception. The moduleResolution setting can accept import paths that native Node would reject. ### Where do you run the examples on these pages? Either place works for the plain snippets. In a browser, right-click any page, choose Inspect, open the Console tab, and paste the code in. For longer code, and for anything using import or export, save a file such as app.js next to a package.json containing type module, then run node app.js. ## Continue Learning JavaScript - Return to the [JavaScript guide](/programming/javascript/) for where the language fits, the full learning path, and what to build first. - Read [TypeScript Types](/programming/typescript/types/) when you want the compiler to catch the mismatches this topic teaches you to spot by hand. - [JavaScript vs TypeScript](/programming/javascript-vs-typescript/) covers what types add and when the extra step earns its keep. --- ## Modules, Imports, and Exports in JavaScript URL: /programming/javascript/language-basics/modules-imports-and-exports/ Cluster: Programming > Javascript For new code, use ES modules with named exports, and keep a default export for a file that does one thing. You need Node installed to run the examples, and nothing else. A module is a file that keeps its own variables to itself and publishes a named list of what other files may use. Before modules, every script shared one global namespace and load order decided everything, which is where a lot of old advice about script tags comes from. ## Named and Default Exports Here is the smallest version that runs. Create a folder, add a file called `prices.js`, and put a named export and a default export in it: ```js // prices.js export const TAX_RATE = 0.1; export function withTax(amount) { return amount * (1 + TAX_RATE); } export default function formatPrice(amount) { return `$${amount.toFixed(2)}`; } ``` Then a second file that uses it. The default export is imported by whatever name you choose, and named exports are imported inside braces using their exact names: ```js // app.js import formatPrice, { withTax, TAX_RATE } from "./prices.js"; console.log(TAX_RATE); console.log(withTax(50)); console.log(formatPrice(withTax(50))); ``` One more file is needed before Node will read these as modules. Add a `package.json` in the same folder containing exactly this, which tells Node to treat every `.js` file here as an ES module: ```json { "type": "module" } ``` Now run `node app.js` in a terminal opened in that folder: ```text // node output: // 0.1 // 55.00000000000001 // $55.00 ``` The middle line is floating point arithmetic, not a module problem, and the [values and equality guide](/programming/javascript/language-basics/values-variables-and-equality/) covers why it happens. Everything not exported, such as any helper you add to `prices.js` without the `export` keyword, stays private to that file. The practical difference between the two forms is which mistakes are possible. A named import must request a name the module exports, so a typo fails to load with a message naming the missing export. A default import's local name is yours to choose, so renaming it is never a typo. The only failure available is importing a default from a module that has none. Both failures happen at the same moment, before any of your code runs. Native ES modules are linked first, and a request the module cannot satisfy stops the program there rather than quietly binding `undefined`. ## Import Paths That Resolve Change the import in `app.js` to drop the extension, which is how a great deal of older tutorial code is written: ```js import { withTax } from "./prices"; ``` ```text // node output: // Error [ERR_MODULE_NOT_FOUND]: Cannot find module ... imported from ... app.js // Did you mean to import "./prices.js"? ``` Node's native ESM file resolution requires the extension on a relative path, and it names the fix directly in the error. A browser is looser: `./prices` is a perfectly valid URL and works if the server serves JavaScript there. Bundlers add the extension for you. That is why the same import can work in a bundled project and fail the moment you run the file yourself. Three path shapes exist and they mean different things. A path starting with `./` or `../` is relative to the current file, and a path starting with `/` is absolute. The third shape is a bare name such as `"react"`. That names a package rather than a file, which Node looks up in `node_modules`, and which the browser cannot resolve without an import map or a bundler. ## ES Modules Against CommonJS Node had its own module system for years before the language got one. That system is CommonJS, and it uses `require` and `module.exports`: ```js // the CommonJS form, still everywhere in existing projects const { withTax } = require("./prices.js"); module.exports = { withTax }; ``` You will meet both, so the deciding rule is worth stating plainly. In Node, a `.js` file is an ES module when the nearest `package.json` says `"type": "module"`, and CommonJS when it says `"type": "commonjs"`. The `.mjs` and `.cjs` extensions force one format each regardless. With no `type` field at all the file is ambiguous, and current Node inspects the source: a file containing ESM-only syntax is treated as a module. Setting `type` explicitly is the way to stop guessing, which is why the example above did. Inside Node, mixing them mostly works now. An ES module can import a CommonJS file, and a CommonJS file can `require` an ES module: Node added that in v22.0.0 and v20.17.0, and dropped the experimental flag in v22.12.0 and v20.19.0. Browsers are a different matter, since CommonJS source needs a build step before a browser can run it at all. The one refusal is top-level `await`, and it applies to the whole graph rather than the file you name. Requiring a module that imports a module that awaits fails just the same: ```text // node output, requiring a module that awaits at the top level: // Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph // with top-level await. Use import() instead. ``` New code should use ES modules. They are the language standard and the tooling has settled on them. Portable module syntax runs in a browser unchanged, though a Node file that imports bare package names or `node:` built-ins still needs a bundler before a browser can load it. ## Modules in the Browser The browser reads the same syntax, with one attribute on the script tag: ```html ``` That attribute changes four things at once. The file may use `import` and `export`, it runs in strict mode automatically, its top-level variables stay out of the global scope, and it is deferred, meaning it runs after the HTML is parsed rather than blocking it. One rule catches everyone at least once. Module scripts are fetched over HTTP, so opening the page as a `file://` URL fails with a CORS error. Serve the folder over a local server instead, which any framework dev command already does for you. ## Load Order and Hoisting Imports do not run in the order they appear among your other statements. In a straightforward graph with no cycles and no top-level `await`, a file's dependencies are linked and evaluated before that file's own first line: ```js // side.js console.log("side effect in module"); export const value = 1; // order.js console.log("first line of order.js"); import { value } from "./side.js"; console.log("value is", value); ``` ```text // node output: // side effect in module // first line of order.js // value is 1 ``` The imported file's own output came first, even though the import statement sits below a `console.log`. That is import hoisting, and it is why a module with side effects runs them whether you use its exports or not. Moving an import among ordinary statements therefore changes nothing. Reordering imports against each other can still change the order those side effects run in. A module is also evaluated once per resolved URL, no matter how many files import it, because the result is cached. That makes a module a reasonable place for shared setup, and a poor place for anything you expect to happen fresh on each call. The URL is what identifies it, not the file on disk. Separate workers, or the same file requested with a different query string, each get their own evaluation. ## Pitfalls and Debugging SyntaxError: Cannot use import statement outside a module. Usually Node read the file as CommonJS: add `"type": "module"` to the nearest `package.json`, or rename the file to `.mjs`. In the browser, the same message means the script tag is missing `type="module"`. The other cause is placement. A static import must sit at the top level of a module, so writing one inside a function or a block raises the same error. ReferenceError: require is not defined. The opposite mistake, and it usually means CommonJS code was pasted into an ES module file. Convert the line to an `import`, or move that file to a `.cjs` extension if it needs the old system. ERR_MODULE_NOT_FOUND on a relative import. A missing file extension is the common cause, and Node's message suggests the corrected path. The same error also covers a wrong path, a package that was never installed, and a package that does not expose that subpath. An import that resolves locally and fails on the server. Usually letter case. A filename that resolves on a Mac can fail on a Linux build machine, where the filesystem is case-sensitive. SyntaxError: The requested module does not provide an export named 'x'. A named import asking for something the file does not export, or a default import from a file with no default. Native ESM rejects this while linking rather than binding `undefined`. Log the whole namespace with `import * as prices from "./prices.js"` to see what the file publishes. A circular import failing on a value that clearly exists. When two files import each other, one is still mid-evaluation when the other reads it. In ES modules that gives `ReferenceError: Cannot access 'x' before initialization` for a `const`, `let`, or class export, while an exported `var` reads as `undefined`. The fix is structural. Move the shared piece into a third file that both import, rather than trying to order the imports around each other. ## Frequently Asked Questions ### Should you use named or default exports? Named exports are the safer default. The requested name has to exist, so a typo is caught while the module is linked rather than surfacing later as a mystery. A local alias is still available through ``{`import { exportedName as localName }`}``. Reserve a default export for files whose whole purpose is one thing, such as a single component. ### Why does my import need a .js extension? Native Node ESM requires it on a relative path, so ./prices resolves to nothing there. A browser is different. That specifier is a valid URL and works when the server serves JavaScript at it. Bundlers such as Vite and webpack add the extension for you, which is why the requirement looks inconsistent. ### Do you need a build tool to use modules? No. Every current browser supports import and export directly through a script tag with type="module", and Node runs them natively. A bundler still helps on larger sites by reducing the number of requests and by resolving package names, but nothing requires one to start. ### Can one project mix ES modules and CommonJS? In Node and compatible bundlers, yes, and many projects and their dependency graphs do. An ES module can import a CommonJS file, and current Node lets require load an ES module too, as long as nothing in that graph uses top-level await. Browsers cannot run CommonJS source without a build step first. ## Related - [JavaScript Language Basics](/programming/javascript/language-basics/) for the full topic overview - [Objects, Arrays, and Destructuring](/programming/javascript/language-basics/objects-arrays-and-destructuring/) for the braces syntax named imports borrow from - [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/) for the functions a module usually exports - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## Objects, Arrays, and Destructuring in JavaScript URL: /programming/javascript/language-basics/objects-arrays-and-destructuring/ Cluster: Programming > Javascript If a variable is a luggage tag, an object is a labeled shelf and an array is a numbered row of pigeonholes. Assigning one to a second variable ties a second tag to the same shelf rather than building a new one. That sharing is the difference between code that works and code that changes something across the room. ## Object Literals An object literal is a set of named slots written in braces. Values can be any type, including other objects and functions: ```js const product = { name: "Desk lamp", "list price": 49.5, stock: { warehouse: 3 }, }; console.log(product.name); console.log(product.stock.warehouse); ``` ```text // node output: // Desk lamp // 3 ``` Keys are strings or symbols, and anything else you write as a key is converted, normally to a string. Quotes are only needed when the key is not a valid identifier, as with `"list price"`. API responses frequently use keys with hyphens or spaces, and those keys need bracket access rather than a dot. Two shorthands appear constantly in modern code. When a variable name matches the key you want, ``{`{ name }`}`` means ``{`{ name: name }`}``. Methods can drop the `function` keyword, so ``{`{ save() { } }`}`` is a method named `save`. ## Property Access Dot access uses a fixed name written into the code. Bracket access takes any expression, which is what you need when the key is in a variable: ```js const product = { name: "Desk lamp", "list price": 49.5 }; const field = "name"; console.log(product[field]); console.log(product["list price"]); console.log(product.colour); ``` ```text // node output: // Desk lamp // 49.5 // undefined ``` The last line is the behavior that defines JavaScript objects. When a lookup finds nothing on the object or anything it inherits from, it returns `undefined` rather than raising an error, so a typo in a property name fails silently at the point it is made and loudly somewhere else entirely. That is what makes `?.` from the [values and equality guide](/programming/javascript/language-basics/values-variables-and-equality/) so useful on nested data. Reading `product.stock.warehouse` when `stock` is missing throws immediately, while `product.stock?.warehouse` evaluates to `undefined` and lets you supply a fallback. Objects are not arrays, so they have no `map` or `filter` of their own. Three built-in helpers convert an object into something you can loop over: ```js const product = { name: "Desk lamp", price: 49.5, stock: 3 }; console.log(Object.keys(product)); console.log(Object.values(product)); for (const [key, value] of Object.entries(product)) { console.log(key, "=", value); } ``` ```text // node output: // [ 'name', 'price', 'stock' ] // [ 'Desk lamp', 49.5, 3 ] // name = Desk lamp // price = 49.5 // stock = 3 ``` `Object.entries` is the one you reach for most, because it hands you a key and a value together and everything you know about array methods then applies. `Object.fromEntries` goes the other way, turning a list of pairs back into an object after you have filtered or transformed it. ## The Array Methods Worth Knowing Four methods cover most day-to-day array work. Each takes a callback and each answers a different question: ```js const orders = [ { id: "A1", total: 40, status: "paid" }, { id: "A2", total: 12, status: "refunded" }, { id: "A3", total: 95, status: "paid" }, ]; console.log(orders.filter((order) => order.status === "paid").map((order) => order.id)); console.log(orders.reduce((sum, order) => sum + order.total, 0)); console.log(orders.find((order) => order.total > 500)); ``` ```text // node output: // [ 'A1', 'A3' ] // 147 // undefined ``` `filter` keeps the elements whose callback returns something truthy and always returns an array. `map` transforms every element and returns an array of the same length. `reduce` folds the whole list into one value, starting from the second argument, which is `0` here. `find` returns the first matching element, or `undefined` when nothing matches, as the last line shows. That `undefined` is the origin of a great many "cannot read properties of undefined" errors, because the next line usually reads a property of the result. Two more are worth adding early. `some` and `every` answer yes-or-no questions about a list without building anything, and `forEach` runs a callback purely for its side effects and returns `undefined`. Reaching for `forEach` when you meant `map` is why some code ends up pushing into an array by hand. For plain membership there is no callback at all. `list.includes(value)` returns a boolean, and `list.indexOf(value)` returns the position or `-1` when the value is absent. Older code tends to use `indexOf` with a `!== -1` check, which `includes` replaced. Because `filter` and `map` both return arrays, they chain. The first line in the example above reads as one sentence, left to right: keep the paid orders, then take their ids. Chaining stays readable for two or three steps, and a chain longer than that usually wants intermediate variables with names. ## Destructuring Destructuring pulls values out of an object or array into named variables in one line. It is pure convenience, and it is used so widely that unfamiliarity with it makes modern code unreadable: ```js const order = { id: "A1", total: 40, status: "paid" }; const { id, total, status: state } = order; console.log(id, total, state); const [first, ...rest] = ["A1", "A2", "A3"]; console.log(first, rest); ``` ```text // node output: // A1 40 paid // A1 [ 'A2', 'A3' ] ``` Object destructuring matches by key name, so order does not matter. The colon in `status: state` renames on the way out, which resolves collisions when two objects both have an `id`. Array destructuring matches by position instead. The most useful place for it is a function signature, where it names exactly which fields the function uses and supplies defaults in the same breath: ```js function describe({ id, total = 0, currency = "AUD" }) { return `${id}: ${total} ${currency}`; } console.log(describe({ id: "A9" })); ``` ```text // node output: // A9: 0 AUD ``` The caller passed one field and the other two came from defaults. This is exactly how React components receive props, so the pattern is worth being comfortable with before any framework code arrives. Destructuring also nests, which is how people dig into an API response in one line. It is powerful and it gets unreadable quickly, so it is worth knowing how to read even if you write it sparingly: ```js const response = { data: { items: [{ id: "A1" }] }, meta: {} }; const { data: { items: [firstItem] }, meta: { page = 1 } } = response; console.log(firstItem, page); ``` ```text // node output: // { id: 'A1' } 1 ``` Read it from the outside in: reach into `data`, then into `items`, then take position zero and call it `firstItem`. The default on `page` filled in because `meta` had no such key. A default cannot save you from a missing intermediate level, so destructuring `data` when `data` itself is absent still throws. ## Spread and Rest The three dots do two opposite jobs, and which one you get depends on where they sit grammatically. Spread appears in argument lists and inside array or object literals, unpacking a value into them. Rest appears in parameter lists and in binding patterns, collecting whatever is left: ```js const base = { theme: "dark", pageSize: 20 }; const merged = { ...base, pageSize: 50 }; console.log(merged); ``` ```text // node output: // { theme: 'dark', pageSize: 50 } ``` Later keys win, which makes this the standard way to apply an override without touching the original. The same syntax works on arrays, where `[...listA, ...listB]` concatenates and `[...list]` makes a copy. Rest is the form you already saw in the destructuring example and in the rest parameters of the [functions guide](/programming/javascript/language-basics/functions-callbacks-and-closures/). Same three dots, opposite direction: in a pattern that receives values they gather, and in a literal or a call that produces values they scatter. ## Copy Against Mutate Assigning an object hands over a value that identifies the shelf, not the shelf's contents. Two variables can therefore identify the same shelf, and writing through one is visible through the other, while pointing one variable somewhere else leaves the other alone. Spread makes a copy, but only of the top level: ```js const original = { theme: "dark", filters: { tag: "js" } }; const shallow = { ...original }; shallow.filters.tag = "css"; console.log(original.filters.tag); const deep = structuredClone(original); deep.filters.tag = "html"; console.log(original.filters.tag, deep.filters.tag); ``` ```text // node output: // css // css html ``` Editing the nested object through the copy changed the original, because both copies pointed at the same nested object. `structuredClone`, built into modern browsers and Node, copies the whole structure, and the second edit stayed contained. You will also meet `Object.assign(target, source)`, which copies the same own enumerable properties one level deep. It writes into the target you pass, so `Object.assign(original, patch)` modifies `original` rather than returning something new. The two are not interchangeable beyond that. `Object.assign` assigns, so it triggers any setter already on the target, while spread always creates plain data properties. Neither copies the source's prototype or its non-enumerable properties. `structuredClone` is not a universal answer either. It copies plain data, arrays, dates, maps, and sets, and handles cycles, but it throws on functions. A class instance comes back as a plain object, losing its prototype, its getters, and settings such as read-only. For plain data it is still the shortest correct option. The older `JSON.parse(JSON.stringify(value))` trick silently loses dates and undefined values. Array methods split the same way, and the split is not obvious from the names. `sort`, `reverse`, `splice`, `push`, and `pop` change the array in place. `map`, `filter`, `slice`, and `concat` return a new one and leave the original alone. ```js const tags = ["b", "a"]; console.log(tags.toSorted()); console.log(tags); console.log(tags.sort()); console.log(tags); ``` ```text // node output: // [ 'a', 'b' ] // [ 'b', 'a' ] // [ 'a', 'b' ] // [ 'a', 'b' ] ``` The newer `toSorted`, `toReversed`, and `toSpliced` methods do the non-mutating version of each. They are available in current browsers and in Node 20 and later, so check your minimum supported version before relying on them. ## When to Reach for Map and Set A plain object works as a lookup table until its keys stop being simple strings. A `Map` accepts any value as a key without converting it to a string, and reports its own size: ```js const counts = new Map(); counts.set("js", 1).set("css", 2); console.log(counts.get("js"), counts.size, counts.has("php")); const key = { id: 1 }; counts.set(key, "object key"); console.log(counts.get(key), counts.get({ id: 1 })); ``` ```text // node output: // 1 2 false // object key undefined ``` The last line shows why object keys are compared by identity, not contents. The second lookup used a different object that happens to look identical, and found nothing. A plain object would have converted both keys to the string `"[object Object]"` and collided instead. Key matching is not quite `===`, in two useful ways. A `Map` can find a `NaN` key, which `===` could never match, and it stores `-0` as `0`. `Set` is the same idea for values with no duplicates. `new Set(list)` followed by `[...unique]` is the shortest way to deduplicate an array of primitives, and `set.has(value)` is a membership test that does not scan the list. Plain objects still win in two places. They are what `JSON.parse` hands back for a JSON object, and what `JSON.stringify` serializes directly, whereas a `Map` or `Set` needs conversion first because its entries are not ordinary properties. They also read better for fixed, known-in-advance shapes such as a configuration block. ## Pitfalls and Debugging TypeError: Cannot read properties of undefined after a find. The classic sequence is `orders.find(...)` returning `undefined`, then the next line reading `.total` from it. Check the result before using it, or use `?.` plus a fallback when a miss is legitimate. Object keys silently becoming strings. Setting `obj[1]` and reading `obj["1"]` gives the same slot, and `Object.keys` reports `['1']`. When numeric or object keys must stay distinct, use a `Map`. A sorted list appearing somewhere you did not sort. `sort` mutates, so sorting an array you were handed changes it for whoever else holds it. Copy first with `[...list].sort()` or use `toSorted`. Numbers sorted in the wrong order. `[10, 9, 100].sort()` gives `[10, 100, 9]`, because the default comparison converts elements to strings. Pass a comparator: `list.sort((a, b) => a - b)`. Nested state changing in two places at once. Almost always a shallow copy where a deep one was needed. In React and other frameworks that detect updates by comparing references, this shows up as a UI that does not update, because the top-level reference never changed even though the contents did. Comparing two objects with ===. Identical contents are not equal, since the comparison is by identity. Compare the fields that matter. Comparing serialized forms is a shortcut that only holds for fully JSON-safe data with matching key order, since functions, dates, `Map`, `Set`, and `undefined` are dropped or transformed on the way through. ## Frequently Asked Questions ### When should you use a Map instead of an object? Use a Map when keys are added and removed at runtime, when you need the count, or when keys are not strings. An object converts most key values to strings, so the number 1 and the string "1" collide. A Map accepts any value as a key, including objects. ### Why did editing my copy change the original? Spread and Object.assign copy one level deep. The top-level properties are new, but a nested object or array is the same value in both copies, so editing it shows up in each. Use structuredClone for a deep copy of plain data, or rebuild the nested level explicitly. ### What's the difference between forEach and map? Both run a function once per element. forEach returns undefined and exists for side effects, while map collects each return value into a new array of the same length. If you are building a new list, use map; if you are only doing something, use forEach. ### Does sort change the original array? Yes. sort, reverse, splice, push, and pop all modify the array in place, which is why a sorted list sometimes changes something you did not expect. Copy first with a spread, or use the newer toSorted, toReversed, and toSpliced methods, which return a new array. ## Related - [JavaScript Language Basics](/programming/javascript/language-basics/) for the full topic overview - [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/) for the callbacks every array method takes - [Modules, Imports, and Exports](/programming/javascript/language-basics/modules-imports-and-exports/) for splitting these helpers across files - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## Values, Variables, and Equality in JavaScript URL: /programming/javascript/language-basics/values-variables-and-equality/ Cluster: Programming > Javascript Think of a JavaScript variable as a luggage tag rather than a suitcase. The tag carries a name and you can tie it to any bag you like, swapping bags without ever changing the tag. The type belongs to the bag, never to the tag, and that single arrangement explains most of what follows. A handful of pieces turn up constantly in real code: declaring values, the primitive types, how JavaScript converts between them, the two equality operators, what counts as true in a condition, the difference between `null` and `undefined`, and the two operators that handle missing data cleanly. ## Declaring Values with `let` and `const` There are two declarations worth using. `const` ties the tag to one bag permanently, and `let` lets you retie it later: ```js let visitCount = 0; visitCount = visitCount + 1; console.log(visitCount); const siteName = "CodeWalkers"; siteName = "Other"; ``` ```text // node output: // 1 // TypeError: Assignment to constant variable. ``` Notice that the error arrives while the code runs, not before it. The first two lines executed and printed happily, and the program only stopped when it reached the reassignment. The engine does check the whole file for syntax errors first, but a bad reassignment is not one of those. The word `const` is doing less than it looks like it does. It locks the tag to one bag, and says nothing about what is inside the bag: ```js const settings = { theme: "dark" }; settings.theme = "light"; console.log(settings.theme); ``` ```text // node output: // light ``` The object was declared `const` and its contents changed anyway, because the binding never moved. If you need the contents locked, `Object.freeze(settings)` does that, though it only freezes the top level. The practical convention on most teams is short. Reach for `const` first, switch to `let` the moment you find yourself needing to reassign, and treat a file full of `let` as a hint that some values are being reused where a new name would read better. ## The Primitive Types JavaScript has seven primitive types, plus objects for everything else. The `typeof` operator reports which one a value is: ```js console.log(typeof "hello"); console.log(typeof 42); console.log(typeof true); console.log(typeof undefined); console.log(typeof null); console.log(typeof Symbol("id")); console.log(typeof 9007199254740993n); ``` ```text // node output: // string // number // boolean // undefined // object // symbol // bigint ``` Six of those answers are what you would expect. The seventh, `typeof null` returning `"object"`, is a bug from the first version of the language in 1995 that was never fixed because fixing it would break existing sites. Every JavaScript engine reproduces it deliberately. Two of these types deserve a note. There are two numeric types, and `number` is the one you will use for nearly everything, covering integers and decimals alike as 64-bit floating point. That format is why `0.1 + 0.2` comes out as `0.30000000000000004`. `BigInt`, written with a trailing `n`, is the second numeric type, for whole numbers too large for that format to hold exactly. The two do not mix: most arithmetic combining a `number` and a `BigInt` throws a `TypeError` rather than converting. Objects and arrays are compared by identity, so two separate objects with identical contents are never equal to each other. That surprises people the first time they compare two arrays. Primitives compare by value under `===`, with two documented oddities: `NaN` is not equal to itself, and `-0` and `0` are equal. ## Type Coercion When an operator gets types it did not expect, it usually converts rather than complains. This is called coercion, and it is the source of the language's strangest reputation. A few combinations do throw instead, notably `BigInt` mixed with `number` and any attempt to convert a symbol to a number: ```js console.log("5" + 3); console.log("5" - 3); console.log(1 + true); console.log(0.1 + 0.2); ``` ```text // node output: // 53 // 2 // 2 // 0.30000000000000004 ``` The `+` operator is the odd one out. Each side is converted to a primitive first, and if either result is a string it concatenates, so `"5" + 3` becomes the string `"53"`. The other arithmetic operators convert to a numeric type instead, which is why `"5" - 3` quietly gives you `2`. The luggage-tag picture explains where this bites. A form field always hands you a string, so a quantity read from an input and added to a running total concatenates instead of adding. Convert at the boundary with `Number(value)` and the rest of your code works on the type it expects. The conversion functions have edges of their own: ```js console.log(Number("49.5")); console.log(Number("49.5kg")); console.log(Number("")); console.log(parseFloat("49.5kg")); ``` ```text // node output: // 49.5 // NaN // 0 // 49.5 ``` On strings, `Number` is strict: the whole trimmed string must be valid numeric syntax or the result is `NaN`, and an empty or whitespace-only string becomes `0`. It converts non-strings too, turning `true` into `1` and `null` into `0`. `parseFloat` and `parseInt` are lenient and read as far as they can. That is what you want for `"49.5kg"` and dangerous for a field that should have been numeric all along. The floating point result in the earlier example is not coercion, but it belongs to the same family of surprises. Money and other exact quantities are safest handled in whole cents as integers, then formatted for display at the end. ## Comparing Values with Double and Triple Equals JavaScript ships two equality operators. `===` never converts, and `==` may convert one side when the two types differ, though same-type operands take the strict path and `null` against `undefined` has a rule of its own: ```js console.log(1 == "1"); console.log(1 === "1"); console.log(null == undefined); console.log(null === undefined); console.log(0 == ""); console.log(NaN === NaN); ``` ```text // node output: // true // false // true // false // true // false ``` The default is `===`, because it means what it appears to mean. The conversion table behind `==` is real, documented, and consistent, but almost nobody carries it in their head, so a comparison that depends on it is a comparison the next reader has to look up. There is one `==` case worth keeping. Writing `value == null` is true for both `null` and `undefined`, which is usually exactly the question you are asking when you check whether something is missing. The last line is not a typo. `NaN`, the value you get from arithmetic that has no numeric answer, is not equal to itself under either operator. Test for it with `Number.isNaN(value)`. ## Truthiness An `if` statement has to reach a yes or no answer about any value you hand it, so every value is either truthy or falsy. The falsy list is short and worth memorizing, because everything else is truthy: ```js for (const value of [0, "", null, undefined, NaN]) { console.log(Boolean(value)); } console.log(Boolean("0")); console.log(Boolean([])); console.log(Boolean({})); ``` ```text // node output: // false // false // false // false // false // true // true // true ``` The falsy values are `false`, `0`, `-0`, `0n`, `""`, `null`, `undefined`, and `NaN`. That is the whole language list. Everything else, including the string `"0"`, an empty array, and an empty object, passes an `if` check. Browsers add one historical exception that no other object shares. The legacy `document.all` is an object, yet it is falsy and loosely equal to `undefined`, kept that way so old sites keep working. The empty array is the one that catches people. Checking `if (results)` to find out whether a search returned anything is always true, because an empty array is still an array. Ask about the length instead, with `if (results.length > 0)`. ## `null` Against `undefined` JavaScript has two ways to say "no value". The useful convention is that `undefined` means nobody set this and `null` means somebody set it to nothing on purpose, though the language enforces none of that: code can assign `undefined`, and an API can hand you `null` nobody chose: ```js const user = { name: "Ada", nickname: null }; console.log(user.nickname); console.log(user.email); console.log(JSON.stringify(user)); ``` ```text // node output: // null // undefined // {"name":"Ada","nickname":null} ``` The engine produced `undefined` on its own for the property that was never there. It also does this for a function parameter you did not pass, and for a function that returns nothing. The last line shows a practical difference. `JSON.stringify` keeps a `null` property and drops an `undefined` one entirely, so the choice changes what reaches your API. Most teams settle on one convention: `null` for a field that exists and is deliberately empty, and `undefined` left to the engine. ## Nullish Coalescing and Optional Chaining Two operators exist specifically for the missing-value problem. `??` supplies a fallback, and `?.` stops a property lookup safely partway down: ```js const settings = { pageSize: 0, theme: null }; console.log(settings.pageSize || 20); console.log(settings.pageSize ?? 20); console.log(settings.theme ?? "dark"); console.log(settings.profile?.avatar); ``` ```text // node output: // 20 // 0 // dark // undefined ``` The first two lines are the whole argument for `??`. The `||` operator falls back on any falsy value, so a deliberate page size of `0` was overwritten with `20`. The `??` operator only falls back on `null` and `undefined`, so the real value survived. The last line reads a property of a property that does not exist. Without `?.` that line throws a `TypeError`; with it, the chain short-circuits and evaluates to `undefined`. The same syntax works on function calls with `obj.method?.()` and on indexes with `list?.[0]`. Optional chaining is easy to over-apply. Every `?.` you add is a claim that a value legitimately might be missing, so scattering them through code where the value is always present hides real bugs behind a silent `undefined`. ## Pitfalls and Debugging TypeError: Cannot read properties of undefined (reading 'x'). The most common runtime error in JavaScript, and the message names the property you tried to read, not the thing that was missing. Read it as "something before `.x` was undefined", then log the chain one step at a time until you find which step returned nothing. ReferenceError: x is not defined. Different error, different cause. This one means the name itself does not exist in scope, usually from a typo, a missing import, or browser-only code such as `document` running in Node. ReferenceError: Cannot access 'x' before initialization. A close relative, and a different fix. It means a `let`, `const`, or class binding was reached before its declaration ran, which is usually a read above the declaration line but also happens through a function called too early or an import cycle. Numbers arriving as strings. A text input's `value`, a `URLSearchParams` entry, and a Node `process.env` entry are strings or absent, so a total that reads `"1020"` instead of `30` means a concatenation happened. Convert once at the boundary with `Number(value)`, and check the result with `Number.isNaN` before trusting it. Comparing two objects or arrays. `[1, 2] === [1, 2]` is `false`, because each literal creates a separate value and the comparison is by identity. Compare the fields you care about, or compare serialized forms when the key order is under your control. A falsy check that was meant to be a missing check. `if (!count)` treats a real count of zero as absent, and `if (!name)` treats an empty string the same way. When the question is "was this provided", ask `if (count == null)` instead. ## Frequently Asked Questions ### Should you ever use double equals? One case is useful: comparing against null with == also matches undefined, which is often what you mean when checking whether a value is missing. Everywhere else, use === so the comparison does not depend on conversion rules you have to remember. ### Why does 0.1 plus 0.2 not equal 0.3? JavaScript numbers are 64-bit floating point values, and 0.1 and 0.2 have no exact binary representation. The sum comes out as 0.30000000000000004. Compare money and other exact quantities by working in whole cents, or compare within a small tolerance rather than exactly. ### Is var still allowed? Yes, and it will keep working, since the language does not remove old syntax. It behaves differently from let. It is function-scoped rather than block-scoped and it can be redeclared. New code uses const by default and let when a value must change. ### When should you use ?? instead of ||? Use ?? whenever 0, an empty string, or false are legitimate values. The || operator falls back on any falsy value, so a page size of 0 or a search box left empty silently becomes the default. The ?? operator only falls back on null and undefined. ## Related - [JavaScript Language Basics](/programming/javascript/language-basics/) for the full topic overview - [Functions, Callbacks, and Closures](/programming/javascript/language-basics/functions-callbacks-and-closures/) for how functions capture the variables declared here - [Objects, Arrays, and Destructuring](/programming/javascript/language-basics/objects-arrays-and-destructuring/) for the values that are not primitives - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## Files, Environment Variables, and Small Automations URL: /programming/javascript/node/files-environment-variables-and-small-automations/ Cluster: Programming > Javascript Small Node automations rest on four skills: reading and writing files from a script, listing a folder and picking out the files you want, building paths that survive being run from the wrong place, and keeping secrets out of your code with environment variables. This is where Node stops being a curiosity and starts saving you an afternoon. ## Reading and Writing a File The promise-based file interface lives at `node:fs/promises`, and the `node:` prefix says plainly that this is a built-in rather than something installed. Two functions cover most work: ```js import { writeFile, readFile } from "node:fs/promises"; await writeFile("notes.txt", "First line\nSecond line\n", "utf8"); const text = await readFile("notes.txt", "utf8"); console.log("characters read:", text.length); console.log(text); ``` ```text // node output: // characters read: 23 // First line // Second line ``` The `"utf8"` argument is what makes `readFile` return text. Leave it out and you get a buffer of bytes instead, which is right for images and wrong for anything you meant to read. `writeFile` replaces the whole file and creates it if it is not there, so it never appends by accident. Use `appendFile` when adding to the end is what you want. Both are promises, which is why every line above is awaited, and the [promises guide](/programming/javascript/async/promises-and-async-await/) covers what `await` is doing. ## When the File Is Not There A missing file rejects the promise, so an unhandled one ends the script. Catching it lets you say something useful instead: ```js import { readFile } from "node:fs/promises"; try { await readFile("does-not-exist.txt", "utf8"); } catch (error) { console.log("code:", error.code); console.log("message:", error.message); } ``` ```text // node output: // code: ENOENT // message: ENOENT: no such file or directory, open 'does-not-exist.txt' ``` That `code` property is the part to branch on. `ENOENT` means no such file, and it is a stable identifier, whereas the message wording is for humans and is not a contract. Knowing the code lets you separate the case you expected from the case you did not. A missing configuration file might reasonably fall back to defaults, while a permission error should stop the script and say so. ## Listing a Folder `readdir` gives you the names inside a folder as plain strings, which array methods then filter: ```js import { readdir } from "node:fs/promises"; const entries = await readdir("inbox"); console.log("everything:", entries); console.log("markdown only:", entries.filter((name) => name.endsWith(".md"))); ``` ```text // node output: // everything: [ 'notes.txt', 'one.md', 'two.md' ] // markdown only: [ 'one.md', 'two.md' ] ``` Two details matter more than they look. The names are names, not paths, so joining them to the folder is your job before you can open one. And the order is whatever the file system reports, not a guaranteed alphabetical sort, so call `sort()` when the order is part of what you promise. `readdir` also does not descend into subfolders unless you ask, with its `recursive` option, which was added in Node 18.17 and 20.1. Starting without it is usually the safer choice, because a script that edits files should be explicit about how far it reaches. ## Paths and the Folder You Happen to Be In This is the single most common reason a working script suddenly stops working. A relative path such as `"inbox"` is resolved against the folder the terminal is in, which is not necessarily the folder the script is in: ```js import { join, resolve, extname, basename } from "node:path"; console.log("cwd:", process.cwd()); console.log("this file's folder:", import.meta.dirname); console.log("join:", join("inbox", "one.md")); console.log("beside this file:", join(import.meta.dirname, "inbox", "one.md")); console.log("extname:", extname("report.final.md")); console.log("basename:", basename("inbox/one.md", ".md")); ``` ```text // node output, run from inside the script's own folder: // cwd: /private/tmp/example/fsdemo // this file's folder: /private/tmp/example/fsdemo // join: inbox/one.md // beside this file: /private/tmp/example/fsdemo/inbox/one.md // extname: .md // basename: one ``` Run the identical file from one folder up and only the first line changes, to `/private/tmp/example`. The script's own folder stays put, which is exactly why it is the reliable anchor. A script that reads `"inbox"` relatively fails from that second location, and the failure is the same `ENOENT` as a missing folder: ```text // node output, running the folder-listing script from one level up // (working directory /private/tmp/example, script in /private/tmp/example/fsdemo): // Error: ENOENT: no such file or directory, scandir 'inbox' // exit code: 1 ``` The rule that avoids all of this: build paths from `import.meta.dirname` when the target belongs to the script, and take them from an argument when the target belongs to whoever is running it. Use `join` rather than gluing strings together with slashes, since it handles the separators for you. One boundary on that anchor. `import.meta.dirname` exists only in ES modules loaded over the `file:` protocol, arrived in Node 20.11 and 21.2, and stopped being experimental in 22.16 and 24.0. In CommonJS the equivalent is the long-standing `__dirname`. ## Environment Variables and `.env` Files Environment variables are values handed to a program by whatever started it, and they are how a key stays out of your source code. In Node they arrive on `process.env`: ```js console.log("API_KEY:", process.env.API_KEY); console.log("OUTPUT_DIR:", process.env.OUTPUT_DIR ?? "reports"); ``` ```text // node output, with nothing set: // API_KEY: undefined // OUTPUT_DIR: reports // and set for one run only, as: API_KEY=abc123 node env.js // API_KEY: abc123 // OUTPUT_DIR: reports ``` A missing variable is `undefined` rather than an error, which is why the default in the second line uses `??`. Setting one in front of the command applies to that run alone, and the next run sees nothing again. That prefix form is POSIX shell syntax, so macOS and Linux only. PowerShell has no per-command prefix. Set `$env:API_KEY = "abc123"` first, and remove it afterwards with `Remove-Item Env:\API_KEY` if it should not linger in the session. Typing them every time gets old, so Node reads a file of key and value pairs directly. No package is needed for this: ```bash API_KEY=from-the-env-file OUTPUT_DIR=build/reports ``` ```text // node output, run as: node --env-file=.env env.js // API_KEY: from-the-env-file // OUTPUT_DIR: build/reports ``` One behavior is worth knowing before it confuses you. A value already set in the environment wins over the file: running the same command with `API_KEY=from-the-shell` in front of it prints the shell value while still taking `OUTPUT_DIR` from the file. That precedence is documented, and it lets the launching environment override a file-supplied value. You can also pass `--env-file` more than once, and later files override earlier ones. `--env-file` arrived in Node 20.6. A missing file stops startup with an error rather than being ignored. Use `--env-file-if-exists` when the file is optional, which prints a note and continues with the variable unset. It arrived in Node 22.9 and was backported to later Node 20 releases, so run `node --help` if you are on an older line and need to know. And add `.env` to your gitignore file before you put anything real in it. ## A Complete Bulk Edit Script Here is everything above in one job worth doing: rewriting insecure links across a folder of Markdown files. It takes the folder as an argument, supports a dry run, and reports what it did: ```js import { readdir, readFile, writeFile } from "node:fs/promises"; import { join } from "node:path"; const folder = process.argv[2]; const dryRun = process.argv.includes("--dry-run"); if (!folder) { console.error("Usage: node fix-links.js [--dry-run]"); process.exit(1); } const names = (await readdir(folder)).filter((name) => name.endsWith(".md")); let changed = 0; for (const name of names.sort()) { const path = join(folder, name); const before = await readFile(path, "utf8"); const after = before.replaceAll("http://codewalkers.dev", "https://codewalkers.dev"); if (before === after) { console.log("unchanged:", name); continue; } changed += 1; if (dryRun) { console.log("would update:", name); } else { await writeFile(path, after, "utf8"); console.log("updated:", name); } } console.log(`${changed} of ${names.length} files ${dryRun ? "would change" : "changed"}`); ``` Run the preview first. Here the folder holds three files, two of them containing the old link: ```text // node output, run as: node fix-links.js posts --dry-run // would update: first.md // would update: second.md // unchanged: third.md // 2 of 3 files would change // exit code: 0 ``` The dry run leaves the files untouched. Running it without the flag rewrites them: ```text // node output, run as: node fix-links.js posts // updated: first.md // updated: second.md // unchanged: third.md // 2 of 3 files changed // first.md before: Read more at http://codewalkers.dev/start. // first.md after: Read more at https://codewalkers.dev/start. ``` Run it a second time and it reports all three unchanged. A script safe to run twice is a script you can rerun after fixing one file by hand. Three habits are doing the work here, and they transfer to every automation you write. Fail loudly with a usage message and a non-zero exit code when the input is missing. Compare before and after rather than writing unconditionally, so unchanged files stay untouched. And say what happened, per file and in total. ## Pitfalls and Debugging ENOENT on a path you can see in your editor. The likeliest cause is the terminal being somewhere else, since relative paths follow the terminal. Print `process.cwd()` at the top of the script to confirm before changing anything else. readFile returned something that is not text. The encoding argument was left out, so you have a buffer. Pass `"utf8"`, or call `toString("utf8")` on what came back. writeFile emptied the file. It replaces the entire contents, so writing a variable that was never populated leaves nothing. Read, transform, then write, and use `appendFile` when adding to the end is the intent. The script ate a file it should not have. The filter was too broad or reached deeper than expected. Run against a copy first, add the dry run before the write, and leave `recursive` off unless you meant it. The env file was loaded and the value is still the old one. Something in the shell already set that name, and the existing value wins. Check with the same script that prints it, in a shell where you have not set it. The script exited without running and reported a missing env file. Check the filename after `--env-file`, or switch to the if-exists form when the file is optional. ## Frequently Asked Questions ### Why does the script work in the editor but not from another folder? A relative path is resolved against the folder the terminal is in, not the folder the file lives in. Running the same script from one level up therefore looks for the data somewhere it is not. Build paths from import.meta.dirname when the target sits beside the script, which ES modules have had since Node 20.11. ### Should the env file be committed? No. It exists precisely to hold the values you do not want in the repository, so add it to your gitignore file before you put anything real in it. Commit an example file listing the names with placeholder values instead, so the next person knows what to supply. ### Do you still need a package for env files? Not for the ordinary case. Node reads a file of key and value pairs itself when you pass the env-file flag, and repeating the flag layers several files, with later ones winning. A package is still worth it if you need variable expansion inside the file, validation of what was loaded, or different loading semantics. ### Is a dry run worth the extra code? For anything that rewrites files, yes, and it is usually a handful of lines. It turns an irreversible step into a preview you can read before committing to it. The habit matters most on the runs where you were confident, because those are the ones nobody checks first. ## Related - [JavaScript with Node](/programming/javascript/node/) for the topic overview and the terminal basics behind every command here - [Running JavaScript with Node](/programming/javascript/node/running-javascript-with-node/) for process.argv, the output streams, and the exit codes this script uses - [package.json, npm, and Scripts](/programming/javascript/node/package-json-npm-and-scripts/) for naming this command so nobody has to retype it - [Promises and async/await](/programming/javascript/async/promises-and-async-await/) for what await is doing on every file call above - [Objects, Arrays, and Destructuring](/programming/javascript/language-basics/objects-arrays-and-destructuring/) for the filter and sort steps on the folder listing - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## JavaScript with Node URL: /programming/javascript/node/ Cluster: Programming > Javascript Node runs JavaScript on your computer outside a web page. You can use it to work with files and folders and build small tools that you run yourself. It is also the part of JavaScript most likely to stop somebody who is otherwise doing fine. The obstacle is rarely the code. It is the terminal window, the version numbers, and the sense that everyone else was handed a manual you never got. Start by opening a terminal window and checking which version of Node is installed before running a JavaScript file. ## What Node Is and What It Is Not Node is a program you install, the same way you installed your browser. You hand it a file of JavaScript and it runs that file, prints whatever the file prints, and then stops. What it adds to JavaScript is access to your machine. A browser deliberately refuses to let a web page read your documents folder, and Node is not sandboxed that way, which is exactly why it can do the useful work in this topic. It is not unrestricted, though. Your files are still governed by the operating system's own permissions, and Node has an opt-in Permission Model of its own, available since Node 20 and stable from Node 22.13. What it removes is the page. There is no `document`, no `window`, no clicking and no styling, so the code from the [DOM topic](/programming/javascript/dom/) fails immediately if you paste it into a Node file. The language is the same in both places; the surroundings are not. ## Opening a Terminal and Finding Your Way A terminal is a window that takes typed commands instead of clicks. On macOS it is the Terminal app, on Windows it is Windows Terminal or PowerShell, and on Linux it is usually just called Terminal. Your code editor almost certainly has one built in, which is the easiest place to start. When it opens you get a prompt: some text, then a blinking cursor waiting for you. You type one line, press Enter, and the program either prints something or prints nothing and gives the prompt back. Nothing happens until you press Enter. Four commands carry you through this whole topic. `pwd` prints the folder you are currently in, `cd` followed by a folder name moves you into it, `cd ..` moves you back up one level, and `ls` lists what is there. Those four are the macOS and Linux spellings, and Windows PowerShell accepts `pwd`, `cd`, and `ls` too. Two habits save more time than any command. Pressing Tab completes a half-typed folder name, which also proves you spelled it right. Pressing Ctrl and C together usually interrupts whatever is running and hands the prompt back. ## Checking Whether You Already Have Node Before installing anything, ask. Type this and press Enter: ```bash node --version ``` ```text // terminal output (your version number may differ): // v24.16.0 ``` A version number means Node is installed and you can move straight to the first guide. Anything that reads as an unknown-command error means Node is not installed, or not on the list of places your shell searches. The wording depends on the shell. macOS and Linux shells say `command not found`, while PowerShell says the term `is not recognized`. The pattern is the same either way. You see your typed word, then a complaint that it is unknown. That failure is worth recognizing rather than fearing. Nothing has broken, and nothing has been installed by accident. To install it, download the current Long Term Support release from [nodejs.org](https://nodejs.org/) and run the installer for your operating system. Once it finishes, close the terminal, open a new one, and run `node --version` again. ## JavaScript Node Topics - [Running JavaScript with Node](/programming/javascript/node/running-javascript-with-node/) covers checking your install and understanding version numbers, running a file with `node file.js`, what `"type": "module"` changes about `import`, reading the arguments you typed after the filename with `process.argv`, the difference between the two output streams, and the exit code a script leaves behind. - [package.json, npm, and Scripts](/programming/javascript/node/package-json-npm-and-scripts/) covers the fields in that file and which ones Node itself reads, why dependencies and devDependencies are separate lists, naming long commands as `npm` scripts so you stop memorizing them, what a lockfile is for and why it belongs in version control, and when `npx` is the right tool. - [Files, Environment Variables, and Small Automations](/programming/javascript/node/files-environment-variables-and-small-automations/) covers reading and writing files with `fs/promises`, building paths that survive being run from the wrong folder, keeping keys out of your code with environment variables and `--env-file`, and one complete bulk-edit script with a dry run, shown before and after it changes the files. Read them in that order. The second assumes you can run a file, and the third assumes you can read the arguments the second one teaches you to pass. ## The Rule Everything Here Depends On A Node script runs from top to bottom, once, and the process then ends as soon as it has no work left that is still holding it open. Nothing is remembered between runs unless the script wrote it somewhere. That explains most of what surprises people here. A variable you set is gone, while a change you made to a file is not, because the file is on disk. Some things do hold the process open: a pending timer, a file watcher, an open socket. A server is simply the most familiar of them, not a special case. It also explains the shape of every script in this topic. Each one takes input from its arguments or a file, does its work, prints what it did, and exits with a code saying whether it worked: ```js const args = process.argv.slice(2); if (args.length === 0) { console.error("Usage: node check.js "); process.exit(1); } console.log("Checking", args[0]); ``` ```text // node output, run as: node check.js report.txt // Checking report.txt ``` ## Common Pitfalls - Running the command in the wrong folder: `node app.js` looks for `app.js` in the folder you are currently in, not the folder you last had open in your editor. Run `pwd` and `ls` first, and the [files guide](/programming/javascript/node/files-environment-variables-and-small-automations/) covers the same trap for the paths inside your code. - Expecting browser objects to exist: `document`, `window`, and `alert` are provided by the browser, so a Node run stops on the first line that uses one. The [DOM topic](/programming/javascript/dom/) says the same thing from the other direction. - Assuming a file finished successfully because it printed something: output and success are separate signals. The [running guide](/programming/javascript/node/running-javascript-with-node/) covers exit codes, which is the answer the terminal acts on. - Copying an installation command from a forum post: a command that installs software deserves more suspicion than a command that prints a version. Take install instructions from [nodejs.org](https://nodejs.org/) and package instructions from the package's own documentation. ## Common Questions ### Do you need to learn the terminal to use Node? You need about five commands, not a course. Changing folder, listing what is in one, running a file, reading the answer, and stopping a program covers nearly everything on these three guides. Editors such as VS Code have a terminal panel built in, so you can stay in one window while you learn them. ### Which version of Node should you install? Take the current Long Term Support release from nodejs.org unless a project tells you otherwise. Through Node 26 only even-numbered majors became Long Term Support, and from Node 27 every major is planned to reach it. A version manager lets each project pin its own version rather than fighting over one global install. ### Is Node a language? No, and the distinction saves confusion later. The language is JavaScript, and Node is a program that runs it outside a browser, adding the abilities a browser deliberately withholds such as reading your files. Learning Node is learning what it adds, not learning a second language. ### Can you run your browser code in Node? Only the parts that are pure JavaScript. Functions, objects, promises, and array methods behave identically, because they belong to the language rather than to the browser. Anything that touches a page, such as document or window, does not exist in Node and fails on the line that uses it. ### What do you do when a command seems stuck? Press Ctrl and C together in the terminal window, which usually interrupts the running program. It sends an interrupt signal rather than forcing an exit, so a program that installs a handler can catch it and carry on. A prompt that returns means the program has ended and the terminal is ready again. ## Continue Learning JavaScript - Return to the [JavaScript guide](/programming/javascript/) for where the language fits, the full learning path, and what to build first. - Read [JavaScript Language Basics](/programming/javascript/language-basics/) for the values, functions, objects, and modules every script here is written from. - Read [JavaScript Async](/programming/javascript/async/) for the promises behind every `await` in the file-handling examples. - Read [JavaScript and the DOM](/programming/javascript/dom/) for the browser half of the language, which is the half Node does not have. - Read [TypeScript](/programming/typescript/) when a script grows big enough that you want the compiler checking it, and note the boundary: the runtime and the package manager are taught here, the compiler is taught there. --- ## package.json, npm, and Scripts URL: /programming/javascript/node/package-json-npm-and-scripts/ Cluster: Programming > Javascript Six questions about `package.json` come up early: what it is for, which of its fields matter early, why dependencies are split into two lists, how to turn a long command into a short named one, what a lockfile protects you from, and when `npx` is the right tool. ## What package.json Actually Is It is a plain JSON file sitting at the top of a project, and two different audiences read it. Node reads a small number of fields when it runs your code, and npm reads the rest when it installs or runs things. You can create one by hand. `npm init -y` writes a starter version for you, filling in the folder name and sensible defaults, and either route gives you the same kind of file. Because it is JSON, the rules are strict: double quotes on every key, no trailing commas, and no comments. Break one of those and Node stops before your code runs at all: ```text // node output, with a trailing comma in package.json: // Error: Invalid package config /path/to/package.json. // exit code: 1 ``` The error names the config file rather than any line you wrote, which is the clue. Nothing in your JavaScript is wrong. ## The Fields Worth Knowing A generated file has more fields than you need on day one. These are the ones that do visible work: ```json { "name": "notes-cleaner", "version": "1.0.0", "type": "module", "main": "index.js", "scripts": { "start": "node index.js" }, "dependencies": {}, "devDependencies": {} } ``` `name` and `version` identify the project, and only matter to anyone else if you publish it. `main` names the entry file for code that imports your project as a package, which a private script never does. `type` is the field Node itself acts on, and it decides whether the `.js` files in this package use `import` or `require`. It is not the only thing that can decide: a `.mjs` or `.cjs` extension overrides it, and syntax detection settles files it never covered. The [running guide](/programming/javascript/node/running-javascript-with-node/) covers both. Your own code can read the file too, with an import attribute: ```js import pkg from "./package.json" with { type: "json" }; console.log("type field:", pkg.type); ``` ```text // node output: // type field: module ``` That `with { type: "json" }` spelling became stable in Node 20.18.3 and 22.12, after arriving experimentally in 20.10. Older material shows `assert { type: "json" }` instead, which Node 22 removed, so treat it as a spelling you may meet rather than one to copy. ## Dependencies Against devDependencies Two lists, one question: does the running program need this, or only the person working on it? `dependencies` holds packages your code imports and therefore needs wherever it runs. `devDependencies` holds the tooling around it, such as test runners, linters, and type checkers, which the deployed program never imports. The distinction earns its keep in two places. A production install can skip the development list with `npm install --omit=dev`, or by setting `NODE_ENV=production`, which makes that the default. The split also documents intent for the next person reading the file. `npm install ` adds to the first list and `npm install --save-dev ` adds to the second. Moving an entry between the two objects is all it takes to correct one. Get it wrong in one direction and it bites in production. Something your code imports at runtime, filed under `devDependencies` by a stray `--save-dev`, is exactly what the production install above leaves out, so the deployed program dies with a module-not-found for a package that worked perfectly on your machine. One habit is worth adopting before you add anything. Check what you are installing. A package name is easy to typo, easy to imitate, and installing one can run its own code on your machine, so read the package's own page rather than a forum post. ## Scripts, Naming the Commands You Keep Retyping The `scripts` object maps a short name to a command line. It exists so that nobody has to remember the long form or keep it in a text file somewhere: ```json { "scripts": { "start": "node index.js", "clean": "node fix-links.js posts", "check": "node --check index.js" } } ``` Run one with `npm run clean`. Running `npm run` with no name lists the scripts that exist, which makes the file itself the documentation for how a project is operated. Two details save confusion. Arguments meant for your script go after a double dash, as in `npm run clean -- --dry-run`, because otherwise npm reads them as its own. The second is that a script runs with the project's locally installed tools already on the path. A tool installed as a dependency can therefore be named directly rather than by its full path. Scripts also pass through the exit code of the command they ran, which is why the [exit-code discipline](/programming/javascript/node/running-javascript-with-node/) from the previous guide matters here. A failing script stops a chain of them rather than letting it continue quietly. ## The Lockfile and Why It Is Committed Version numbers in `package.json` are frequently ranges rather than exact values. A caret accepts later releases that keep the same leftmost non-zero number, which means the same manifest can resolve to different versions on different days. The lockfile removes most of that variability. It records the exact version of every package that was installed, including the ones your dependencies pulled in themselves. It is not automatically frozen, though. `npm install` honors the locked versions only while they still satisfy `package.json`, and re-resolves and rewrites the lock when they do not. `npm ci` is the strict command: it installs exactly what the lockfile says and fails outright when the two disagree. Three consequences follow, and they are the whole of the practical advice. Commit the lockfile, since a teammate without it gets a different tree from the same manifest. Do not hand-edit it, because the tool regenerates it. And use one package manager per project. npm, pnpm, and Yarn each write their own lockfile format, and two of them disagreeing about one tree is worse than either alone. `node_modules` is the opposite case: large, machine-specific, and rebuildable from those two files. It belongs in your gitignore file from the first day. ## npx and Running Something Once `npx` ships with npm and runs a package's command without adding it to your project. It prefers a locally installed copy and otherwise fetches the package to run it, which is the point when you want a scaffolding tool once and never again. That convenience is also the caution. The command executes code from a package you may never have looked at. So it deserves the same scrutiny as an install. Check the exact name against the project's own documentation, since a near-miss spelling is the oldest trick in this ecosystem. For anything you run more than twice, prefer a named script over a remembered `npx` line. The script is committed, reviewable, and identical for everyone on the project. ## Pitfalls and Debugging Invalid package config. The JSON is malformed, usually a trailing comma or a missing quote, and Node reports it before running anything of yours. Open the file and check the punctuation rather than the code. npm run says the script is missing. Either the name is spelled differently in the file, or the terminal is not in the folder holding that `package.json`. Running `npm run` with no name lists what is there. Your flags reached npm instead of your script. Without the double dash, npm treats them as its own arguments and your script sees nothing. `npm run clean -- --dry-run` is the form that passes them through. Two lockfiles in one repository. Someone installed with a different package manager. Decide which one the project uses, delete the other lockfile, and say so in the readme. A dependency that only the tooling needs. Nothing breaks, but a production install carries weight it does not use. Move the entry into `devDependencies`. Works locally, module-not-found once deployed. The opposite mistake: something imported at runtime sits in `devDependencies`, so a production install omits it. Check which list names the package before hunting the deployment. A dependency added because one line of it was useful. Every package is code you now trust and maintain. Check whether the standard library already covers it before the install, not after. ## Frequently Asked Questions ### Should node_modules go into version control? No. It is large, machine-specific, and rebuildable from the manifest and the lockfile, which is exactly why those two are committed instead. Add it to your gitignore file on the first day, because removing it from history afterwards is far more work. ### What does the caret in a version number mean? A caret accepts later releases that keep the same leftmost non-zero number, so it treats them as compatible. That flexibility is why two people can resolve different versions from the same manifest. Pinning exact versions removes the guesswork, and it is the safer default when a surprise upgrade would be expensive. ### Why does a fresh install give different versions to a teammate? Almost always because the lockfile was not committed, so each install resolved the ranges independently. Commit it, and use npm ci where the tree must match exactly, since plain npm install re-resolves whenever the lock no longer satisfies package.json. The other cause is two people using different package managers. ### Do you need package.json for a single script? No, a single file runs perfectly well without one. Add it as soon as you want import and export declared rather than detected, a named command instead of a memorized one, or your first dependency. Those three needs tend to arrive together. ### Is npm the only option? No. npm ships with Node, and pnpm and Yarn read the same manifest while storing files and resolving versions differently. Pick one per project and stay with it, because mixing them produces two lockfiles that disagree about the same tree. ## Related - [JavaScript with Node](/programming/javascript/node/) for the topic overview and the terminal basics behind every command here - [Running JavaScript with Node](/programming/javascript/node/running-javascript-with-node/) for the module system this file's type field selects, and for exit codes - [Files, Environment Variables, and Small Automations](/programming/javascript/node/files-environment-variables-and-small-automations/) for the kind of script a named entry usually points at - [Modules, Imports, and Exports](/programming/javascript/language-basics/modules-imports-and-exports/) for what import and export do once the type field allows them - [TypeScript](/programming/typescript/) for the compiler side of a project, which is configured separately and taught there - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## Running JavaScript with Node URL: /programming/javascript/node/running-javascript-with-node/ Cluster: Programming > Javascript Running a first Node script means checking your install, running a JavaScript file from a terminal, knowing what the `"type"` field changes, reading the arguments somebody typed after your filename, sending output to the right stream, and leaving an exit code the terminal can act on. ## Confirming Your Install and Reading the Version Start by asking whether Node is already there. Two commands answer it, and neither changes anything: ```bash node --version which node ``` ```text // terminal output (yours will differ): // v24.16.0 // /Users/example/.nvm/versions/node/v24.16.0/bin/node ``` The second line shows which copy of Node you are running, which matters once more than one is installed. The path shown here comes from a version manager, so yours will differ. On Windows the equivalent is `where.exe node` or `(Get-Command node).Source` in PowerShell. Write `where.exe` rather than bare `where`, because PowerShell uses `where` as an alias for `Where-Object`; bare `where node` is correct in `cmd.exe`. If the shell instead reports an unknown command, saying `command not found` on macOS and Linux or that the term `is not recognized` in PowerShell, Node is not installed or is not on the list of places your shell searches. In a POSIX shell the exit code for that is 127, which is that specification's convention rather than a universal value. PowerShell raises a `CommandNotFoundException` instead, and its `$LASTEXITCODE` only tracks native programs and PowerShell scripts. Version numbers read as major, minor, patch. Through Node 26, only even-numbered majors became Long Term Support releases and received fixes for years; from Node 27 every major is planned to reach Long Term Support. Either way the practical advice is the same. Take a currently supported Long Term Support release. Installers live at [nodejs.org](https://nodejs.org/). ## Running Your First File Put one line in a file called `hello.js`: ```js console.log("Hello from Node"); ``` Then, in a terminal whose current folder is the folder holding that file, run it: ```bash node hello.js ``` ```text // node output: // Hello from Node ``` Node read the file, ran it top to bottom, printed the line, and ended. There is nothing else running afterwards and nothing to shut down. If the answer is a `MODULE_NOT_FOUND` error naming your file, Node looked in the folder your terminal is in rather than the one your editor has open. List the folder contents and check the name, including the `.js` extension. ## What `"type": "module"` Changes JavaScript has two module systems. Which one a `.js` file uses is decided by the nearest `package.json` when that file supplies a `"type"`, and by syntax detection when nothing does. A `.mjs` or `.cjs` extension overrides both. Setting the field is one line: ```json { "type": "module" } ``` With that in place, a `.js` file uses `import` and `export`, and `require` stops existing. That is not a warning but a hard error, and the message is unusually helpful: ```js const fs = require("node:fs"); ``` ```text // node output: // ReferenceError: require is not defined in ES module scope, you can use import instead // This file is being treated as an ES module because it has a '.js' file extension // and package.json contains "type": "module". To treat it as a CommonJS script, // rename it to use the '.cjs' file extension. ``` One current behavior is worth stating precisely, because a great deal of older advice contradicts it. On Node 24, a `.js` file containing `import` with no `package.json` deciding the matter still runs, because Node inspects the syntax and treats it as a module: ```js import { shout } from "./util.js"; console.log(shout("hello")); ``` ```text // node output, with no package.json present: // HELLO ``` That is syntax detection, and it has a boundary worth knowing. It arrived behind a flag in Node 20.10 and 21.1, and is on by default from Node 20.19 and 22.7, so older tutorials calling that exact file a syntax error were right for their version. It applies only to input that is otherwise ambiguous, and only certain syntax triggers it: a static `import` or `export`, `import.meta`, top-level `await`, and some redeclarations. A dynamic `import()` does not, and a file using only `import()` stays CommonJS. Node also documents the feature as release-candidate status carrying a performance cost, since detection means parsing a file twice when the first attempt fails. Declaring `"type": "module"` is therefore still the better habit: it states the intent, avoids the detection pass, and behaves the same on every version. Two extensions override the field whenever you need the other system in one file. A `.mjs` file is always a module, a `.cjs` file is always CommonJS, and a module can import a `.cjs` file. Top-level `await` works in module files, including a plain `.js` file detected as one. The [modules guide](/programming/javascript/language-basics/modules-imports-and-exports/) covers the import syntax itself. ## Reading What the Person Typed Anything typed after the filename arrives in `process.argv`. It is a plain array of strings, and its first two entries are always the Node binary and your script: ```js console.log(process.argv); ``` ```text // node output, run as: node greet.js Ada --loud // [ // '/Users/example/.nvm/versions/node/v24.16.0/bin/node', // '/private/tmp/example/greet.js', // 'Ada', // '--loud' // ] ``` Almost nobody wants those first two, so the usual first line slices them off: ```js const args = process.argv.slice(2); console.log("arguments:", args); console.log("first:", args[0]); ``` ```text // node output, run as: node greet2.js Ada --loud // arguments: [ 'Ada', '--loud' ] // first: Ada ``` Run the same file with nothing after it and the array is empty, so `args[0]` is `undefined` rather than an error. That silence is why the next section matters: a script that quietly proceeds with `undefined` is worse than one that stops and says what it needed. Everything in that array is a string, including numbers, so `"3"` needs converting before you do arithmetic with it. For a flag, `args.includes("--dry-run")` is enough; Node's `util.parseArgs` handles the cases where it is not. ## The Two Output Streams A program has two ways out. Standard output carries the result, and standard error carries messages about the run, which is why `console.log` and `console.error` are not just two colors of the same thing: ```js console.log("this goes to standard output"); console.error("this goes to standard error"); process.stdout.write("no newline of its own"); process.stdout.write("\n"); ``` Run it plainly and all three lines appear together, because the terminal shows both streams. Send the output to a file and they separate: ```bash node streams.js > out.txt ``` ```text // still shown in the terminal: // this goes to standard error // // and out.txt now contains: // this goes to standard output // no newline of its own ``` That is the whole reason for the distinction. Progress notes and warnings belong on standard error so they stay visible when somebody captures your real output, and the same applies when the output is piped into another command. `console.log` adds a newline and formats objects for reading; `process.stdout.write` does neither, which suits building a line in pieces. ## Exit Codes, the Answer Nobody Prints Every run leaves a number behind. Zero means it worked and anything else means it did not, and you read it with `echo $?` immediately after the command on macOS and Linux. On Windows PowerShell the equivalent is `$LASTEXITCODE`. ```js const args = process.argv.slice(2); if (args.length === 0) { console.error("Usage: node check.js "); process.exit(1); } console.log("Checking", args[0]); ``` ```text // node output: // $ node check.js // Usage: node check.js // exit code: 1 // // $ node check.js report.txt // Checking report.txt // exit code: 0 ``` You get the same signal without asking for it. An uncaught error prints a stack trace and exits with 1, and so does a missing file. A script that ran to the end with no error exits with 0 on its own, so there is nothing to add on the successful path. Node's own guidance prefers setting the code over forcing the exit, because `process.exit` can cut off output that has not been written yet. Assign `process.exitCode` and let the script finish on its own: ```js process.exitCode = 1; console.log("work finished, leaving a failure code"); ``` ```text // node output: // work finished, leaving a failure code // exit code: 1 ``` Keep the forced `process.exit(1)` for the case in the example above, where you want to stop before doing any work at all. When something must stop mid-run, throwing is safer than forcing the process down. This is the number every other tool reads. A conditional chain such as `a && b` stops when one link exits non-zero, though commands separated by semicolons carry on regardless, and PowerShell has supported `&&` only since version 7. A continuous integration job reports a failure on exactly the same evidence. Printing the word failed while exiting 0 tells a person one thing and every machine the opposite. ## Three Flags Worth Knowing Early `node -e` runs a snippet with no file at all, which is the quickest way to check what something returns. `node -p` does the same and prints the result: ```bash node -p '2 + 2' ``` ```text // node output: // 4 ``` `node --check` parses a file without running it, which answers whether a problem is a typo or a logic error. It exits 0 on a file that parses and 1 on one that does not, and it reports the line and column of the first syntax error. ## Pitfalls and Debugging Cannot find module, naming your own file. The likeliest cause is the terminal being in a different folder from the file. Check with the folder-listing command, and check the extension too, since `hello` and `hello.js` are different names to Node. require is not defined. The file is being treated as a module, whether by a `"type": "module"` field nearby, an `.mjs` extension, or syntax detection reading an `import` already in the file. Convert the line to `import`, or rename the file to `.cjs` if it needs the older system. Cannot use import statement outside a module. This is the mirror image, and on Node 24 it means something actively decided against modules, most often a `.cjs` extension. Syntax detection only applies when nothing else has decided. An argument arrives as a string. Everything in `process.argv` is text, so a typed `3` is `"3"` and adding to it concatenates. Convert with `Number()` and check the result is not `NaN`, as the [values guide](/programming/javascript/language-basics/values-variables-and-equality/) covers. Invalid package config. A trailing comma or a stray quote in `package.json` stops Node before your file runs at all, with an error naming the file rather than any line of yours. JSON allows no trailing commas and no comments. The script printed an error and the pipeline carried on. Printing is not failing. Call `process.exit(1)`, or throw, so the exit code matches what the message says. ## Frequently Asked Questions ### Why does node file.js say cannot find module? Node looked for that filename relative to the folder your terminal is in, and did not find it. Run the folder listing command first and check the name is there, spelled the same way, including its extension. The same message also appears when an import inside the file points at a path that does not resolve. ### Do you have to add type module? On Node 24 a plain .js file containing import or export usually runs as a module anyway, because Node inspects the syntax when no type field decides it. Declaring it is still worth doing, since it states the intent, removes the guesswork, and behaves the same on older versions that had no detection. ### How do you read flags like dry-run properly? For one or two flags, checking whether the argument array includes them is enough and adds nothing to your project. Once you want values, short forms, and a usage message, Node has a built-in parser in its util module. Reach for an argument-parsing package only when neither of those covers what you need. ### Should a script call process.exit at the end? Not on the successful path, because a script that finishes normally already exits with zero. Calling it early can also cut off output that has not been flushed yet. Node's own guidance is to set process.exitCode and let the script end naturally, keeping a forced exit for when you truly must stop immediately. ## Related - [JavaScript with Node](/programming/javascript/node/) for the topic overview and the terminal basics this page assumes - [package.json, npm, and Scripts](/programming/javascript/node/package-json-npm-and-scripts/) for the file that decides the module system and the scripts that name these commands - [Files, Environment Variables, and Small Automations](/programming/javascript/node/files-environment-variables-and-small-automations/) for what a script does once it can read its arguments - [Modules, Imports, and Exports](/programming/javascript/language-basics/modules-imports-and-exports/) for the import and export syntax itself - [Values, Variables, and Equality](/programming/javascript/language-basics/values-variables-and-equality/) for converting the strings that arrive from the command line - [JavaScript](/programming/javascript/) for the language guide and the wider learning path --- ## Build a Custom API with PHP URL: /programming/php/apis/custom-api/ Cluster: Programming > Php A custom API lets an application expose its data without giving every client direct database access. This is the capstone step of the [PHP web-app path](/programming/php/): the app already has a database-backed screen, comments, and search, and now it exposes a small JSON contract that another client can call. Think of the API as a service counter. The route names the service, the request carries the order, and the response is the receipt. Define each route, its accepted input, and its response before connecting it to the database. Use HTTP status codes, JSON, input validation, and prepared statements so another client can use the API without reading its PHP source. ## Guide Path Start with [dynamic PHP and MySQL pages](/programming/php/mysql/dynamic-sites/), then add [news and comments](/programming/php/mysql/news-comments/) and [search](/programming/php/mysql/search-application/). For the authentication layer itself, read [PHP authentication and sessions](/programming/php/security/auth-sessions/), and keep [PHP security fundamentals](/programming/php/security/fundamentals/) close for the broader boundary rules. ## Start with the Contract HTTP is a protocol built around requests and responses, and many home-grown APIs blur that. The method, path, status code, headers, and body all carry meaning. A small notes API might start with this shape: ```text GET /api/notes POST /api/notes GET /api/notes/{id} PATCH /api/notes/{id} DELETE /api/notes/{id} ``` Document each route’s method, required fields, response and possible errors so callers know how to use it. OpenAPI describes that contract in a language-agnostic way, so humans and tools can understand an HTTP API without reading the server code. Start with the routes clients actually use and keep the specification in step with the code. ## How Does a PHP API Read and Return JSON? A PHP API reads JSON with `json_decode()` and returns it with `json_encode()`. Set the response content type to `application/json` and handle decoding errors before querying the database. ```php listNotes(db(), currentUserId()), ]); } if ($path === "/api/notes" && $method === "POST") { try { $input = readJsonBody(); } catch (JsonException | InvalidArgumentException $error) { jsonResponse(["error" => "Send a valid JSON object."], 400); } if ( ! is_string($input["title"] ?? null) || ! is_string($input["body"] ?? null) ) { jsonResponse([ "error" => "Title and body must be strings.", ], 422); } $title = trim($input["title"]); $body = trim($input["body"]); if ($title === "" || $body === "") { jsonResponse([ "error" => "Title and body are required.", ], 422); } $note = createNote(db(), currentUserId(), $title, $body); jsonResponse([ "data" => $note, ], 201); } if ($path === "/api/notes") { header("Allow: GET, POST"); jsonResponse(["error" => "Method not allowed."], 405); } jsonResponse([ "error" => "Not found.", ], 404); ``` This routing example is not a standalone application. Supply the imported files, database connection, authentication helper and `createNote()` function. The `currentUserId()` helper must reject unauthenticated requests; `createNote()` must save the note for that user. The listing function appears below. Older PHP tutorials often used one file that dispatched on `?action=get_notes`. That could serve a one-client intranet tool, but it became difficult to maintain when another caller needed different data. This sketch shows the underlying request shape. Laravel, Symfony, Slim, and Fat-Free Framework can all give you better routing and request objects once the app grows. For a capstone tutorial, keep the route table visible for the first draft. Writing it by hand once shows where the contract begins and where the PHP plumbing ends. After that, a framework can take over repeated route matching, middleware, request objects, response classes, and controller dispatch. ## Validate Before SQL The API boundary is still a user-input boundary. A JSON body is not safer than a form post just because it looks more structured. If you are building the same rules for browser forms, read [form validation in PHP](/programming/php/forms/spelling-validation/). The transport changes, but the server still owns the decision. ```php ["min_range" => 1], ]); if ($id === false) { throw new InvalidArgumentException("Invalid ID."); } return $id; } ``` Then keep the database boundary behind prepared statements. ```php prepare( "SELECT id, title, created_at FROM notes WHERE user_id = :user_id ORDER BY created_at DESC LIMIT 50", ); $statement->execute(["user_id" => $userId]); return $statement->fetchAll(PDO::FETCH_ASSOC); } ``` If an old API tutorial builds SQL strings from request parameters, borrow its routes and drop its SQL. ## Status Codes Use status codes consistently across the whole API: - `200` for a successful read or update. - `201` when a resource is created. - `204` when a delete succeeds with no body. - `400` for malformed input. - `401` when client authentication is missing or invalid. - `403` when the user is authenticated but not allowed. - `404` when the resource does not exist or should not be revealed. - `409` for a state conflict. - `422` for syntactically valid input that fails validation. The exact split between `400` and `422` matters less than consistency. Document the rule and keep the behavior stable. ## Standardize the Error Body Status codes tell the client what category of thing happened. The JSON body tells the client what to do next. A practical error envelope can stay this small: ```json { "error": { "code": "validation_failed", "message": "Title and body are required.", "fields": { "title": "Title is required." } } } ``` Use that same shape across every handler. A browser can show `message` to the user, a mobile client can branch on `code`, and an automated client can inspect `fields` without scraping a paragraph of prose. Keep sensitive details out of that response. The client needs to know that a record was not found, a token was rejected, or an input field failed validation. It does not need a SQL error, a stack trace, a filesystem path, a raw provider response, or the name of the internal service that failed. ## API Versioning The first version does not need a ceremony around it, but it does need a place to live. Use a versioned path such as `/api/v1/notes` once another system will depend on the contract. Versioning protects clients only when behavior stays predictable. If `/v1/notes` returns `created_at` as an ISO string on Monday and a Unix timestamp on Friday, the version did not protect anyone. The version protects clients only when you treat response fields, field names, required inputs, pagination shape, and error codes as part of the public surface. For a small PHP app, the practical rule is simple enough: - Add fields without breaking existing fields. - Stop sending a field only in a new version. - Change meanings only in a new version. - Keep old versions long enough for real clients to move. - Document deprecation dates before you remove behavior. Version rules become concrete when a mobile build, an automation, and a partner script call the same endpoint. A missing field, renamed status, or changed timestamp format can leave those clients running with bad assumptions. ## Authentication Is Not Optional For a private API, require authentication before the handler reaches business logic. A session cookie can work for same-site browser clients. Tokens are a better fit for external clients, mobile apps, and server-to-server calls. Keep the first version narrow, explicit, and auditable: 1. Require HTTPS for every token-bearing request. 2. Store token hashes rather than plain tokens. 3. Scope tokens to the specific actions they need. 4. Rate-limit login, write, and export endpoints. 5. Log failures without logging secrets or request bodies. Apply the authentication, authorization, and input checks in [PHP security fundamentals](/programming/php/security/fundamentals/) to every protected endpoint. The token check should happen before the handler decides what row to read or write. Keep that boundary in middleware or a small guard function: ```php ["code" => "unauthorized", "message" => "Missing token."]], 401); } $plainToken = substr($header, 7); $token = findTokenByHash($pdo, hash("sha256", $plainToken)); if ($token === null || $token->isExpired()) { jsonResponse(["error" => ["code" => "unauthorized", "message" => "Invalid token."]], 401); } return $token; } ``` Keep authentication separate from authorization in code and prose. Authentication answers who or what is calling. Authorization answers whether that caller can read this note, update this account, or export this report. A valid login does not prevent an authorization bug. Each protected query must still limit access to records that caller is allowed to use. ## Pagination and Rate Limits The first `GET /api/notes` example used `LIMIT 50` because an API without a limit is a database incident waiting for a caller. Use a stable maximum page size, accept a cursor or page token when the result set can grow, and return enough metadata for the client to ask for the next page. For a simple endpoint, offset pagination is acceptable: ```php prepare( "SELECT id, title, created_at FROM notes WHERE user_id = :user_id ORDER BY created_at DESC, id DESC LIMIT :limit OFFSET :offset", ); $statement->bindValue("user_id", $userId, PDO::PARAM_INT); $statement->bindValue("limit", $perPage, PDO::PARAM_INT); $statement->bindValue("offset", $offset, PDO::PARAM_INT); $statement->execute(); return $statement->fetchAll(PDO::FETCH_ASSOC); } ``` Offset pagination gets expensive on deep pages, and it can skip or repeat records while rows are being inserted. When the endpoint matters, move to cursor pagination based on a stable ordered pair such as `created_at` plus `id`. Rate limits belong in the same early bucket. A login endpoint, export endpoint, SMS endpoint, or email-triggering endpoint can cost real money or leak information if the caller can hammer it freely. Start with conservative per-token and per-IP limits, then loosen them when production data proves they are too strict. ## Keep CORS and Logs Boring If a browser on another origin needs to call the API, configure CORS for the exact origins and methods you expect. Do not ship `Access-Control-Allow-Origin: *` on a private API that also accepts credentials. That mistake looks convenient during local testing and then turns into a policy you have to unwind. The logging rule needs the same restraint. Log request IDs, endpoint names, status codes, caller IDs, token IDs, and timing. Avoid request bodies, bearer tokens, cookies, raw provider responses, and anything a user typed into a private field. The point is to debug failures without creating a second copy of the data you were supposed to protect. ## Legacy XML Behind Adapters The old CodeWalkers tutorials included SOAP and XML-RPC lessons because remote procedure calls were a practical way to connect small PHP systems before JSON APIs became the default. That history is useful, but it should not become the shape of a new API. SOAP still appears in payment systems, shipping providers, government systems, and older internal platforms where the WSDL is the contract you inherited. PHP's SOAP extension can read a WSDL and expose remote operations through `SoapClient`, but the simple method call is a little deceptive. You are sending XML over HTTP to a remote system with its own schema, authentication, timeout behavior, and fault model. Keep the WSDL URL, credentials, timeout, SOAP version, and trace settings in one place: ```php $_ENV["SOAP_USER"], "password" => $_ENV["SOAP_PASSWORD"], "soap_version" => SOAP_1_2, "exceptions" => true, "trace" => false, "connection_timeout" => 10, ]); ``` The old debug move was to turn on tracing and print the last request. Do that only in a controlled local environment. SOAP envelopes can carry credentials, customer identifiers, certificate details, and account data that should never land in production logs. XML-RPC has a smaller shape: HTTP POST carries an XML request that names a method and passes values, and the XML response returns either values or a fault. The old PEAR `XML_RPC` pattern wrapped those calls in client, message, and value objects. That pattern was reasonable in its era. In a current PHP app, keep the boundary and drop the package. If you inherit SOAP or XML-RPC, put every encode, decode, fault-code, timeout, retry, credential, and logging choice behind one adapter. The rest of the app should see application values such as `CustomerRecord`, `BookRecord`, or `InvoiceStatus`, not SOAP envelopes or `` payloads. Use SOAP or XML-RPC only when a legacy client or vendor contract still requires it. When you control the new system, build the contract as ordinary HTTP and JSON, document it with OpenAPI, and leave the XML endpoint behind a migration plan. ## Common Pitfalls & Debugging ### The Route and Status Code Disagree A create request succeeds but returns `200`, or a missing record returns a successful JSON body. Each handler chose its response independently. Write the method, path, success status, and error statuses into the contract, then test them together. ### Authorization Stops at Authentication A valid user can request another user's note by changing the ID. The token check identifies the caller, but the query does not constrain the row by that caller's permissions. Include the ownership or authorization condition in every protected read and write. ### A Retry Creates the Resource Twice One client action creates duplicate rows after a timeout. The client retries a request after the server committed the first write but before the response arrived. Give retryable create operations an idempotency key or another unique application constraint. ## Frequently Asked Questions ### Should a DELETE request be idempotent? HTTP DELETE should be idempotent: repeating it should leave the resource in the same deleted state. Responses can differ, such as 204 on the first request and 404 on a later request. The state change, not an identical response, is what matters. ### Should an API version go in the path, a header, or a query string? An API can put its version in a URL path, request header or query string. A path such as /api/v1/notes is easy to see and test. Header versioning keeps URLs stable but requires clients, caches and logging tools to account for that header. ### Should a 204 response return JSON? A 204 No Content response must not return JSON. Use it when the status code fully describes a successful operation, such as a delete, and use a 200 response when the client needs returned data. ### Should an API accept both JSON and XML request bodies? An API should accept XML alongside JSON only when a client needs it. Supporting another format adds parsing, validation and error-handling work. ### Should an API expose sequential database IDs in its URLs? An API can expose sequential database IDs if every request checks permission to access the record. Sequential IDs make nearby IDs easy to guess and can reveal approximate record counts. Random identifiers reduce that predictability but never replace authorization. ### Does a REST API need to implement every HTTP method, like PUT? A REST API does not need to implement every HTTP method. Each resource needs only the methods its operations require. Many APIs never expose PUT at all, using PATCH for partial updates instead, or skip DELETE entirely when a resource is only ever created and read. ### What does JSON_THROW_ON_ERROR actually change in json_encode()? JSON_THROW_ON_ERROR makes PHP’s json_encode() throw a JsonException on encoding failure instead of returning false. Catch the exception in the application’s error handler so the client receives a controlled error response. ## Self-Check Predict the output from this allowlist fallback: ```php "created_at"]; $requestedSort = "unknown"; echo $allowedSorts[$requestedSort] ?? "created_at"; ``` 1. Predict the output: Which column name does the snippet print? 2. Multiple choice: Can a PDO parameter replace a table name, a complete email value, or the keyword `DESC`? 3. Multiple choice: Which status fits a created resource: `200`, `201`, or `204`? 4. Multiple choice: What does authentication establish: caller identity, permission for every row, or both automatically? 5. Predict the output: Which column name prints if `$requestedSort` changes to `"created"`? ### Answers 1. `created_at`. The unknown key misses the allowlist, so the null-coalescing fallback supplies the approved default. 2. A complete email value. Parameters represent data values, while identifiers and SQL keywords must come from application allowlists. 3. `201`. It tells the client that the request created a resource. 4. Caller identity. Authorization still has to check whether that caller may act on the requested resource. 5. `created_at`. The allowed key maps directly to the approved database column. ## Next Steps If the endpoint triggers outbound notifications, read [sending SMS with HTTP APIs](/programming/php/apis/sending-sms/) before wiring a provider into a controller. For the database side of this API, read [PHP and MySQL with PDO](/programming/php/mysql/pdo/) and [SQL transactions and ACID](/programming/sql/transactions/acid/). If you are tempted to hand-write the network request underneath the API, read [using sockets in PHP](/programming/php/networking/sockets/) first. Most APIs should stay at the HTTP contract layer; sockets are for protocol work where the transport itself matters. --- ## Send SMS with PHP and HTTP APIs URL: /programming/php/apis/sending-sms/ Cluster: Programming > Php A [PHP application](/programming/php/) sends SMS through a provider’s SDK or HTTP API. You need server-side credentials, an approved sender and a recipient number. The provider accepts the request first; delivery status arrives later. Start with one test message, then add delivery tracking and retries. Keep consent records and credentials out of public code. ## How Do You Send an SMS with PHP? Use an SMS provider’s PHP SDK to send a recipient number, sender number and message body. For Twilio, install `twilio/sdk` with [Composer](/programming/php/dependencies/composer/), configure your account credentials and use an SMS-capable sender. Twilio’s current trial flow requires a verified recipient; its legacy Console also offers a Virtual Phone for testing. ```bash composer require twilio/sdk ``` Set `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, `TWILIO_FROM_NUMBER` and `SMS_TO_NUMBER` in your local environment. Use a recipient you have permission to message. Save this beside the Composer `vendor` directory: ```php messages->create( getenv("SMS_TO_NUMBER"), [ "from" => getenv("TWILIO_FROM_NUMBER"), "body" => "Your appointment is confirmed for 2:30pm.", ] ); echo $message->sid, PHP_EOL; ``` A returned message SID identifies the request; it does not prove delivery. Handle SDK errors and use the provider’s delivery status before reporting success to a user. Use restricted API credentials for a deployed application where supported. ## Provider API Modern SMS providers expose an HTTP API for outbound messages. The exact fields differ by provider, but the shape is familiar: authenticate, send a `POST`, receive a provider message ID, then watch the status through callbacks or a status endpoint. The following request illustrates the HTTP fields. The example domain is fictional; use your provider’s documented endpoint and payload: ```bash curl -X POST "https://api.sms-provider.example/messages" \ -H "Authorization: Bearer $SMS_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "to": "+15551234567", "from": "CodeWalkers", "body": "Your appointment is confirmed for 2:30pm." }' ``` Do not send that request from browser JavaScript. The API token belongs on the server, in a job worker, or behind a backend endpoint that validates the business action before the SMS provider sees anything. There is no reason to revive [raw socket code in PHP](/programming/php/networking/sockets/) for ordinary SMS work. If the provider has a maintained SDK and it fits your stack, use it. If you use direct HTTP, keep the client small, typed, and boring enough that another developer can review every value being sent. ## What Should a PHP App Store for Each SMS? A PHP app should store the intended recipient, sender, template, consent record, provider message ID and delivery status. These let you check what was requested and what happened afterward. Keep these fields with the notification record: | Field | Why it matters | | Recipient phone number | Normalize the number before sending and store only what the app needs. | | Sender identity | The sender might be a long code, toll-free number, short code, alphanumeric sender ID, or provider messaging service. | | Message template | A template key is easier to audit than free-form text spread across controllers. | | Provider message ID | Store the ID returned by the provider so callbacks and support logs can be joined. | | Delivery status | Accepted, pending, delivered, and rejected states do not mean the same thing, and each provider names them differently. | | Consent record | Keep the opt-in source, timestamp, and purpose close to the send decision. | SMS delivery has more than two states. A successful HTTP response only means the provider accepted the request. ## Keep the Adapter Server-Side The clean application shape is one SMS adapter with one public method. The rest of the app should not know whether the provider is a hosted API, an enterprise carrier gateway, or a test double. ```php $message->to, "sender" => $message->sender, "template" => $message->template, "variables" => $message->variables, ], JSON_THROW_ON_ERROR); curl_setopt_array($handle, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$this->token}", "Content-Type: application/json", "Idempotency-Key: {$message->idempotencyKey}", ], CURLOPT_POSTFIELDS => $payload, ]); try { $response = curl_exec($handle); if ($response === false) { throw new RuntimeException("SMS request failed: " . curl_error($handle)); } $statusCode = (int) curl_getinfo($handle, CURLINFO_RESPONSE_CODE); if ($statusCode < 200 || $statusCode >= 300) { throw new RuntimeException("SMS provider returned HTTP {$statusCode}."); } $data = json_decode($response, true, 512, JSON_THROW_ON_ERROR); if ( ! is_array($data) || array_is_list($data) || ! is_string($data["id"] ?? null) || ! is_string($data["status"] ?? null) ) { throw new RuntimeException("SMS provider returned an invalid response."); } return new ProviderMessage($data["id"], $data["status"]); } finally { curl_close($handle); } } } ``` This adapter requires PHP's cURL extension to run. Generate the idempotency key when the local notification record is created and reuse it for retries only if the provider supports idempotent requests. A custom header alone cannot prevent duplicate messages. ## Consent and Registration SMS has a tighter consent and deliverability story than a lot of old web tutorials admitted. For marketing or recurring notifications, the app needs a clear opt-in path, an opt-out path, and a record that explains why this phone number can be contacted. Providers require verified sender registration in many countries. Check the provider and regulator's current documentation for the destination, sender type, message purpose, consent, and opt-out rules before enabling traffic. Treat that as product work to finish before launch. The message content, sender identity, opt-in language, help text, stop handling, and privacy policy all affect whether the integration is allowed to run cleanly. ## Status Callbacks The first response from the provider is only the beginning of the send. Like scans on a parcel route, status updates describe separate stages rather than one delivery boolean. Good SMS integrations listen for status callbacks or poll status when callbacks are not available. Treat every provider status as part of its documented contract because names and transitions differ. Build the feature around that asynchronous path: 1. Create a local notification record before calling the provider. 2. Send the request with a short timeout and a clear idempotency plan. 3. Store the provider message ID and initial status. 4. Validate provider callbacks before trusting them. 5. Update the local record as statuses arrive. 6. Retry only when the failure mode is safe to retry. Retrying a network timeout is different from retrying a request the provider already accepted. If the app cannot tell those apart, it can send the same customer a duplicate alert. Store the local attempt before sending so support can trace the duplicate without relying on the message body. ## Log Less than You Want Phone numbers and message bodies are sensitive personal data. Keep them out of ordinary application logs regardless of which provider carries the message. A safe application log usually contains these fields: - The local notification ID. - The provider message ID. - A recipient hash or internal user ID. - The template key. - The provider status. - A provider error code when one exists. The unsafe log contains the full phone number, full message body, API token, callback signature secret, or a raw provider response copied into a permanent application log. You will want the raw body during debugging. Build a short-lived, access-controlled debug path if the team truly needs it, then keep the ordinary logs boring. ## Common Pitfalls & Debugging ### The API Token Reaches Browser Code Anyone who opens developer tools can copy the provider token. This happens when browser JavaScript calls the SMS API directly. Keep credentials in the server-side adapter and let a validated application action request the send. ### A Timeout Sends a Duplicate Message One account action can send the same alert twice if the provider accepts a request but the response times out. Record the attempt, use the provider’s idempotency mechanism when available, and check the original request before retrying an uncertain outcome. ### Callback Data Is Trusted Without Validation An untrusted request marks a message delivered or overwrites its error code. The callback route accepts fields without verifying the provider signature. Validate the signature with the provider SDK, then accept unknown extra fields so provider additions do not break the handler. ## Frequently Asked Questions ### Does a successful API response mean the SMS was delivered? A successful API response does not prove SMS delivery. It usually means the provider accepted or queued the message. Delivery happens later through carrier networks, so store the provider message ID and update the local record from validated status callbacks. ### Should PHP send SMS inside the browser request? A PHP SMS handler can call the provider directly for a low-volume action after validation, but a queued job is safer when delays or retries would hold the browser request open. Record the intended notification before the worker sends it. ### Which phone number format should an SMS API use? PHP SMS integrations should format recipient numbers as required by the provider and destination country. Normalize and validate the number before it reaches the provider adapter, but keep provider-specific formatting rules inside the provider integration. ### What should an SMS application log? PHP SMS logs should record the local notification ID, provider message ID, internal user ID or recipient hash, template key, status, and provider error code. Keep full phone numbers, message bodies, tokens, and callback secrets out of ordinary logs. ### Does sending SMS in the US require carrier registration before a provider will deliver it? Application-to-person SMS in the United States generally requires the sending number and use case to be registered and vetted with carriers before a provider delivers messages at volume. Registration programs change over time, so confirm the provider's current process before launch rather than assuming an unregistered number will deliver reliably. ## Next Steps A web app asks a remote service to send a message, and the provider message ID is the tracking number that joins the local record to later status updates. Keep the implementation in a provider adapter with consent and retry rules. If you are exposing your own endpoint first, read [build your own API with PHP](/programming/php/apis/custom-api/). If the provider contract is older and XML-shaped, read the SOAP adapter section in the [PHP API capstone](/programming/php/apis/custom-api/). If the SMS sends from a form or account action, pair the integration with [PHP security fundamentals](/programming/php/security/fundamentals/) before a real phone number enters the system. --- ## Smart Auto Refresh with PHP URL: /programming/php/browser/smart-auto-refresh/ Cluster: Programming > Php A page should update when something changed, and it should not reload itself just because a timer fired. The browser can schedule the refresh, pause hidden tabs, and update the visible component. [PHP](/programming/php/) should answer a narrower question: has the underlying data changed, and if it has, what is the smallest response the browser needs? The browser first requests a version number. If that number has changed, it fetches the new data or reloads the page. A database revision or an ETag can identify the version. Read this after [building a mini chat app with PHP](/programming/php/mysql/mini-chat/) if the next feature is a fresher message list, queue count, dashboard panel, or activity feed. The approach here pairs a PHP endpoint with a small browser-side Fetch loop. The [HTML tutorials hub](/programming/html/) remains useful when the surrounding page structure needs more attention. ## Start with a Version Token A small PHP refresh endpoint returns a version token instead of the whole panel. The browser compares that token with the last version it received. For a database-backed widget, use a monotonic revision that advances on every visible insert, update, and delete. Keep one revision per authorization scope so each user sees a token derived from the same records as the protected data endpoint: ```php prepare( "SELECT revision FROM activity_feed_revisions WHERE user_id = :user_id", ); $statement->execute(["user_id" => $userId]); $value = $statement->fetchColumn(); return $value === false ? "0" : (string) $value; } ``` Bump the revision in the same transaction as every write that changes the user's visible feed, including deletes. Apply the data endpoint's authentication and authorization before querying this token because even a changing version can reveal private activity. `MAX(updated_at)` and `MAX(id)` are acceptable approximations for append-only data. They can miss deletions, updates that preserve the maximum timestamp, and changes to rows below the maximum ID. A cache version can be reliable only when every relevant write advances it. ## Return 304 When Nothing Changed HTTP already has a language for "you have the current version." An ETag lets the browser ask whether a resource changed without downloading the body again. ```php $version, ], JSON_THROW_ON_ERROR); ``` The browser can poll this endpoint and skip the heavier data request when it receives `304 Not Modified`. The status code carries the answer, so no body has to be sent or parsed. `JSON_THROW_ON_ERROR` is available in PHP 7.3 and newer. It turns encoding failures into `JsonException` instead of requiring a later `json_last_error()` check. For private user data, keep `Cache-Control: private, no-cache` or an equivalent policy. The browser may revalidate the response, but shared caches should not reuse another user's status token. ## Return Only the New Rows For a chat box or activity feed, the browser usually needs the rows after the last item it rendered. An incrementing ID makes the cursor logic straightforward. ```php prepare( "SELECT id, message, created_at FROM activity_items WHERE user_id = :user_id AND id > :after_id ORDER BY id ASC LIMIT 50", ); $statement->execute([ "user_id" => $userId, "after_id" => $afterId, ]); $items = $statement->fetchAll(PDO::FETCH_ASSOC); $lastSeenId = $afterId; foreach ($items as $item) { $lastSeenId = max($lastSeenId, (int) $item["id"]); } header("Content-Type: application/json; charset=utf-8"); echo json_encode([ "items" => $items, "last_seen_id" => (string) $lastSeenId, ], JSON_THROW_ON_ERROR); ``` This endpoint returns at most 50 new rows and the last ID it returned. Fetch again with that ID while a full batch is returned. This append-only approach does not report edits or deletions; reload the affected panel when those changes matter. Do not ship raw HTML from this endpoint unless you have a clear sanitization plan. JSON lets PHP send data without deciding how the page should display it. PHP returns data, and the browser renders it with text nodes or a small template. ## File Markers A marker file can suit a tiny app with no database, one server, and a piece of generated content that changes occasionally. In that case, a marker file can be a simple version token. ```php Php [PHP](/programming/php/) configuration controls runtime behavior such as error reporting, memory limits, upload limits, timeouts, and extension settings. Old tutorials often treated one `php.ini` file as the whole system, while current deployments may add PHP-FPM pools, scanned INI files, containers, managed-host settings, and per-directory overrides. Before editing a setting, find the configuration files loaded by the PHP process serving your application. The command line and web server may use different files, which explains why a change can work in one place and appear to do nothing in another. ## Identify the SAPI A SAPI (Server Application Programming Interface) is PHP's interface to the environment running it. CLI, PHP-FPM, the Apache module, and CGI-style installations can load different files or read them at different times. Ask the command-line binary which configuration it loaded: ```bash $ php --ini Configuration File (php.ini) Path: /etc/php/8.5/cli Loaded Configuration File: /etc/php/8.5/cli/php.ini Scan for additional .ini files in: /etc/php/8.5/cli/conf.d ``` The output identifies the configuration files used by command-line PHP. It does not prove that a web request uses the same path because PHP-FPM may run another binary, pool, chroot, or container image. For a temporary local web diagnostic, print the SAPI and loaded file through the actual request path: ```php Php PHP control flow decides which statements run and how often they repeat. Conditionals choose a branch, while loops repeat a block until the data or condition says to stop. For example, an account form might reject an invalid email address, choose a welcome message, and loop through the fields that need checking. These examples build on the basics in our [PHP guide](/programming/php/). Use strict comparisons for application decisions unless type conversion is deliberate. Keep branches large enough to read, stop `switch` cases explicitly, and make every loop's exit condition easy to identify. ## How Do PHP Conditions Compare Values? A PHP condition evaluates an expression as true or false. The expression may compare values, read a boolean, call a function, or combine conditions with `&&` and `||`. ```php = 18 && $country === "AU") { echo "Adult account"; } ``` The condition passes only when the age is at least 18 and the country is AU. Strict identity checks both type and value; loose equality may convert types first: ```php "Draft", "review" => "In review", "published" => "Published", default => "Unknown", }; ``` The same status can select a CSS class instead of a label: ```php "status-draft", "review" => "status-review", "published" => "status-published", default => "status-unknown", }; ``` `match` uses `===`, returns a value, never falls through, and must be exhaustive. If the set is meant to be closed, omitting `default` lets an unexpected state throw `UnhandledMatchError` during testing instead of hiding it under a generic label. ## When Should You Use PHP switch? PHP `switch` is useful when one value selects a block of statements rather than one returned value. The handlers and article ID below are application-provided examples: ```php $status) { echo $position + 1, ": ", $status, PHP_EOL; } ``` `foreach` advances through the array for you. Use a reference only when the loop must modify the original values, then call `unset()` on the reference variable after the loop to avoid carrying it into later code. ### Repeat Until a Condition Changes A `while` loop fits a queue whose length changes as each item is handled: ```php Php Composer installs the libraries a [PHP project](/programming/php/) needs. Its composer.json file lists acceptable versions, while composer.lock records the exact versions selected. Keep that lock file in Git so deployments use the dependencies you tested. The decision that matters is when to run `install` and when to run `update`, and I want every update visible in the lock diff before it is accepted. Autoloading and security checks are separate jobs. ## Before You Start - Use a local PHP project and Composer. - Know basic [PHP classes and namespaces](/programming/php/oop/), and run commands from your project's root directory. - The policy configuration follows Composer 2.10 documentation. Check your Composer version before using those settings; older releases have different audit controls. ## What Is Composer in PHP? Composer is a dependency manager for PHP. It installs the libraries a project needs and generates an autoloader so PHP can find their classes. The official documentation draws the line itself: Composer is "not a package manager in the same sense as Yum or Apt are", because it manages packages "on a per-project basis" and "by default, it does not install anything globally". Two projects on the same server can therefore run different versions of the same library without interfering with each other. It also handles the dependencies of your dependencies. If you ask for a logging library and that library needs an interface package, Composer finds a version of the interface package that satisfies both, and installs it as well. That resolving step is the main thing a dependency manager adds over copying library folders into a project by hand. ### Packagist and Package Names A published library's name has two parts, a vendor and a project, as in `monolog/monolog` or `symfony/console`. Platform requirements are the exception: names like `php`, `ext-pdo`, and `lib-curl` describe your environment rather than a package to download. The vendor half exists to prevent clashes, so two different people can both publish a library called `json` under their own vendor names. Packagist.org is the default repository Composer searches. When your `composer.json` does not register any other repository, a name like `monolog/monolog` is looked up on Packagist. Private or internal packages can come from other repositories that you register with the `repositories` key. ## `composer.json` vs `composer.lock` A Composer project keeps two files at its root, and each one answers a different question. `composer.json` answers "which versions would we accept?", while `composer.lock` answers "which exact versions will every install use?". | File | Written By | Holds | | composer.json | You, or composer require | Direct dependencies and version ranges | | composer.lock | Composer, during update | Resolved packages and exact versions | ### What composer.json Declares This is a small `composer.json` for an application, trimmed to the parts this page teaches. It accepts PHP from 8.4 up to, but not including, 9.0, and it asks for the Monolog logging library, PHPUnit for tests, and an autoloader for the project's own code: ```json { "require": { "php": "^8.4", "monolog/monolog": "^3.12" }, "require-dev": { "phpunit/phpunit": "^13.3" }, "autoload": { "psr-4": { "App\\": "src/" } } } ``` The `php` entry is a platform package, which Composer treats as a requirement it can check but cannot install. The same applies to extensions written as `ext-` names, such as `ext-pdo`, and to system libraries written as `lib-` names. Ordinary entries such as `monolog/monolog` name real packages that Composer downloads. Installed packages can raise the bar further. PHPUnit 13.3 itself requires PHP 8.4.1 or later, so this project cannot install its development tools on PHP 8.4.0 even though its own constraint allows it. ### What composer.lock Records `composer.lock` lists every package in the resolved dependency tree, including the ones you never asked for by name. Each entry carries the exact version plus the download details Composer used, and a Git source entry, when present, includes the commit reference. It records what Composer resolved, which can differ from what sits in `vendor`: `update --no-install` writes the file without installing anything, and `install --no-dev` leaves the development packages locked but not installed. The file also stores a `content-hash` of the relevant parts of `composer.json`. When you run `install`, Composer compares that hash with the current file, so it can detect changes to dependency requirements. ## Adding Your First Dependency The quickest way to add a package is `composer require`, which edits `composer.json`, resolves the new package, updates `composer.lock`, and installs the result in one step: ```bash composer require monolog/monolog ``` With no version given, Composer picks a suitable constraint from the available releases and writes it into `composer.json`. Open the file afterwards and read what it chose, because that constraint decides what every future update is allowed to install. You can also state the constraint yourself: ```bash composer require "monolog/monolog:^3.12" ``` To use the package, load Composer's autoloader once at the entry point of your application, then use the classes by their full names: ```php pushHandler( new StreamHandler('app.log') ); $log->warning('Invoice total is zero'); ``` That script appends a warning line to `app.log`. There is no `require` for any Monolog file, because the autoloader finds each class the first time the code uses it. ## `require` vs `require-dev` The `require` section lists what the application needs to run, while `require-dev` lists what developers need to build and test it. Test frameworks and static analysers belong in `require-dev`. Add a development package with the `--dev` flag: ```bash composer require --dev phpunit/phpunit ``` Both sections are installed by default. A production deploy should pass `--no-dev` to `install`, which skips the `require-dev` packages and the `autoload-dev` rules. That keeps test tooling off the server and shrinks the amount of third-party code an attacker could reach there. Both sections are recorded in the same lock file, in separate `packages` and `packages-dev` lists. Only the root project's `require-dev` counts, so the development dependencies of the libraries you install are never pulled in. ## How Do Composer Version Constraints Work? Composer version constraints specify which releases a project accepts. For example, a constraint can allow patch updates while excluding the next minor release: | Constraint | Allows | Typical Use | | `^1.2.3` | >=1.2.3 <2.0.0 | Most packages | | `^0.3` | >=0.3.0 <0.4.0 | Pre-1.0 packages | | `~1.2.3` | >=1.2.3 <1.3.0 | Patch releases only | | `~1.2` | >=1.2 <2.0.0 | Minor and patch releases | | `1.2.*` | >=1.2 <1.3 | One minor line | | `1.2.3` | Exactly 1.2.3 | Deliberate hard pin | The caret is the one to learn first. It permits every release that semantic versioning promises will not break your code, and it treats pre-1.0 packages more cautiously, because a `0.x` package makes no such promise between minor versions. The documentation calls it "the recommended operator for maximum interoperability when writing library code". A constraint only protects you as far as the package's maintainers follow semantic versioning. A caret range will happily install a new minor release that breaks something, if the maintainers shipped a break without bumping the major version. ### Why the Lockfile Pins More Than composer.json Can An exact version in `composer.json` pins one package. It says nothing about that package's own dependencies, which a full `composer update` is free to move to any version that fits. A project with three direct dependencies can easily install forty packages, and `composer.json` constrains only the three you named. The lock file pins all forty of them to exact versions. That makes it the reproducibility control for an application. It does not establish that those versions are trustworthy; package review and advisory checks still matter. Exact pins also carry a cost the documentation states plainly: if another dependency needs a different version, "the solver will ultimately fail and abort any install or update procedures". For an application, the combination I would recommend is caret ranges in `composer.json` and a committed lock file. The ranges let a deliberate `update` pick up fixes, and the lock file keeps every install identical until you choose to move. The trade-off is that a caret range trusts the maintainers' semantic versioning, as the section above showed. Reserve an exact pin for a package you have a specific, written-down reason to hold back, and accept that the pin can make the solver fail when another package needs a different version. ## When Should You Run `composer install` or `composer update`? Run `composer install` to install versions from an existing lock file. Run `composer update` when you deliberately want Composer to choose versions again and update that file. | Command | Versions Come From | Rewrites the Lock File | | `composer install` | composer.lock | Only if none exists | | `composer update` | Constraints in composer.json | Yes | | `composer require` | The new constraint | Yes | Run `install` after cloning a project or pulling a colleague's changes, because it syncs your `vendor` directory with the committed lock file. Run `update` only when you intend to move to newer versions, and then review and commit the lock file it produces. To move a single package, name it: ```bash composer update monolog/monolog ``` That updates Monolog within its `composer.json` constraint and leaves every other package at its locked version. If one of Monolog's own dependencies also needs to move, add `--with-dependencies` (`-w`). That flag skips any dependency that is also one of your root requirements. When such a package must move as well, use `--with-all-dependencies` (`-W`); the constraint you declared for it in `composer.json` still applies. ### What CI and Production Should Run A build server and a production deploy should install from the lock file and never resolve anything new. A typical production install looks like this: ```bash composer install \ --no-dev \ --no-interaction \ --optimize-autoloader ``` In CI, run `composer validate --strict` before the install. It checks that `composer.json` is valid and that the lock file is up to date with it, and `--strict` turns warnings into a failing exit code. The trimmed example above would not pass that gate. Composer's strict schema wants a `name` and a `description`, and it reports a missing one as a publish error, which fails validation even without `--strict`. A missing `license` is a general warning, which is exactly what `--strict` turns into a failure. Add all three to the example before you use it as a CI gate. For an application that will never be published as a package, `--no-check-publish` removes the name and description requirement. The license warning survives that flag, so the application still needs a `license` entry, and `"proprietary"` is the value Composer's own warning suggests for closed-source software. A pull request that edits `composer.json` without regenerating the lock file then fails in review instead of in production. ## Autoloading Your Own Code with PSR-4 The `autoload` section in the earlier example maps the `App\` namespace prefix to the `src/` directory. Under PSR-4, the class `App\Mail\Mailer` then lives in `src/Mail/Mailer.php`, because the prefix is removed and the rest of the namespace becomes the path: ```php `. ``` If the locked versions still satisfy the edited `composer.json`, the install goes ahead with them, so your change has no effect yet. If they no longer satisfy it, for example because you added a package the lock file does not contain, the install fails. Either way, fix it by running `composer update` for the package you changed, then commit both files. `composer update --lock` refreshes the lock file's hash and package metadata without changing any locked version, so reserve it for when the locked versions really should stay as they are. Editing only the `description` doesn't make the lock file stale in the first place. ### Class Not Found After Adding a Class PHP 8 reports a missing class as `Class "App\Mail\Mailer" not found`. With Composer autoloading, the cause is nearly always one of these: - The namespace in the file does not match its path under the PSR-4 mapping, often through a capitalisation difference that a case-insensitive file system on a laptop hid. - The `autoload` section changed and `composer dump-autoload` was never run. - The server uses an authoritative class map, built with `-a`, before the class existed. - The class lives in `autoload-dev` and the server installed with `--no-dev`. ### Your Requirements Could Not Be Resolved The message "Your requirements could not be resolved to an installable set of packages." means no combination of versions satisfies every constraint at once. The lines Composer prints below it name the packages in conflict. To ask why a particular version cannot be installed, use `why-not`, an alias of `prohibits`: ```bash composer why-not monolog/monolog 4.0 ``` The fix is to loosen the constraint that blocks the upgrade, or to update the package that holds the conflicting requirement in the same command. ### The Lockfile Was Resolved on a Newer PHP Composer resolves versions against the PHP version of the machine running `update`. A developer on a newer PHP than production can lock a release that production cannot run. Declare the production PHP version in the `platform` config, so every update resolves as if it ran on the server: ```json { "config": { "platform": { "php": "8.4.12" } } } ``` The Composer documentation adds one caution about this setting. A faked platform means Composer no longer checks the real one, so run `composer check-platform-reqs` during deployment to confirm the server actually meets the requirements. ## Best Practices and Trade-offs Applications and libraries use Composer differently, because only the root project's lock file is ever used: | Project Type | Constraints | Commit the Lock File | | Application | Caret ranges, rare exact pins | Always | | Library | Caret ranges, as wide as tested | Optional, affects only its own team | A library that pins exact versions forces its choices on every project that installs it, and the solver fails as soon as two packages disagree. Keep the ranges wide in libraries and do the pinning in the application's lock file, where it belongs. - Run `install` everywhere except the one deliberate update, and never run `update` on a production server. - Install production with `--no-dev` and an optimized autoloader. - Gate CI on `composer validate --strict` and `composer audit --locked`. - Approve plugins by name, and record every ignored advisory with a reason. - Turn on Dependabot alerts and malware alerts for the repository, then review its pull requests like any other lock change. ## Keep PHP Dependencies Reproducible Use `composer install` for deployments and reserve `composer update` for deliberate dependency changes. Commit `composer.lock` with the application and review changes before deploying them. Use `composer audit` and Dependabot to check dependencies for reported vulnerabilities. Neither replaces reviewing a new package before adding it. Neither tool catches a tampered package before somebody reports it, so the lock diff review is the part that stays your responsibility. For a PHP application, a committed lock file plus a reviewed update is the minimum I would accept before code reaches a server. ## Frequently Asked Questions ### Should composer.lock be committed to Git? PHP applications should commit composer.lock to Git so developers and servers install the same dependency versions. A library can optionally commit its own lock file, but that file does not control the projects that install the library. ### Should the Composer vendor directory be committed to Git? PHP applications should usually exclude Composer's vendor directory from Git and commit composer.lock instead. Running composer install restores the locked dependencies without adding third-party code to the repository. ### What is the difference between caret and tilde in Composer? Composer caret and tilde constraints allow different version ranges. For example, ^1.2.3 allows versions from 1.2.3 up to, but not including, 2.0.0; ~1.2.3 stops below 1.3.0. With only two version components, ~1.2 also allows releases below 2.0.0. These ranges rely on maintainers following semantic versioning. ### Does composer install ever change composer.lock? Without an existing lock file, composer install resolves dependencies and creates composer.lock. With a lock file, it installs the recorded versions rather than updating them. A relevant composer.json change can trigger a warning or an install failure when the locked requirements no longer fit; install does not repair that mismatch. ### How do you update only one Composer package? Update one Composer package by naming it in the command, such as composer update monolog/monolog. Composer updates that package within the constraint in composer.json and leaves the others at their locked versions. Add --with-dependencies when one of its own dependencies also needs to move, or --with-all-dependencies when that dependency is also one of your own root requirements. ### Where does composer audit get its advisory data? By default, composer audit queries the security advisory API on Packagist.org, or other repositories when your composer.json configures them. Current Composer releases also report abandoned packages and package versions flagged as malware in the same run. ### Does Dependabot support Composer projects? Dependabot supports Composer projects on GitHub. It can open version-update and security-update pull requests, and GitHub's documentation lists Composer among the ecosystems covered by Dependabot malware alerts. Malware alerts are a separate setting, enabled after Dependabot alerts are turned on. ## Self-Check 1. A colleague pushes a change to `composer.lock`. Which command brings your `vendor` directory in line with it? 2. Which versions does `~2.4.1` allow, and which does `^2.4.1` allow? 3. Your `composer.json` pins every direct dependency to an exact version. Why is the lock file still needed? 4. The class `App\Billing\Invoice` sits under the mapping `"App\\": "src/"`. Which file does the autoloader look for? 5. A lock diff after `composer update` shows a package you have never heard of. Which command tells you what required it? 6. Why does a plain `composer install` in CI not replace a separate `composer audit` step? ### Answers 1. `composer install`, which installs the exact versions recorded in the lock file without resolving anything new. 2. `~2.4.1` allows at least 2.4.1 and below 2.5.0. `^2.4.1` allows at least 2.4.1 and below 3.0.0. 3. `composer.json` constrains only the packages you named. A full update can move their dependencies to any version that fits, and only the lock file records the exact versions that were resolved. 4. `src/Billing/Invoice.php`, because PSR-4 removes the `App\` prefix and turns the rest of the namespace into the path. 5. `composer why vendor/package`, the alias of `depends`, which lists every installed package that requires it. 6. `install` only runs an audit when you pass `--audit`. The automatic audit happens after `update`, `require`, and `remove`, which CI normally does not run. ## Next Steps These guides build on Composer dependency and autoloading concepts: - [PHP OOP for beginners](/programming/php/oop/), which uses namespaces and Composer autoloading to organise classes. - [PHP frameworks](/programming/php/frameworks/), where Laravel and Symfony projects both start from a Composer command. - [PHP configuration and php.ini](/programming/php/configuration/), for the runtime settings Composer's platform checks depend on. - [GitHub Dependabot](/appsec/dependabot/), for the alert and update pull request side of dependency security. - The [PHP guide](/programming/php/), for the full list of PHP topics. --- ## Send Email in PHP: SMTP, HTML, and Attachments URL: /programming/php/email/sending-email/ Cluster: Programming > Php To send email from PHP, use a maintained library such as PHPMailer or Symfony Mailer with an SMTP provider. The library formats the message and attachments; the provider handles sending. A successful send means the mail server accepted the message. It does not prove the email reached the inbox. You still need to check delivery failures and configure your sending domain. This guide builds on the request handling and validation covered in our [PHP tutorials](/programming/php/). Email often follows a form submission, account signup, or completed export. ## When Should You Use PHP mail()? `mail()` can send a basic message when the server is already configured with a working mail transfer agent. ```php "CodeWalkers ", "Reply-To" => "hello@example.com", "Content-Type" => "text/plain; charset=UTF-8", ]; mail( "reader@example.com", "Your account is ready", "You can now sign in.", $headers, ); ``` The array form of the additional headers parameter requires PHP 7.2 or newer. Older PHP versions expect the headers as a string, but new code should not recreate that manual formatting on an obsolete runtime. That is fine for understanding the underlying primitive. It is not enough for SMTP authentication, HTML plus text alternatives, attachments, inline images, DKIM, reliable errors, queueing, or provider-specific delivery behavior. The security warning is also plain. If outside data is used in headers, sanitize it so users cannot inject extra headers. The safest version is to avoid hand-building user-controlled headers at all. `mail()` still has a place in a small internal tool on a server you administer yourself. It can also help explain the primitive underneath the abstraction. But it is the wrong boundary for an account system, password reset flow, customer export, invoice, notification feed, or anything that has to be observable when delivery fails. ## Which PHP Mailer Should You Use? PHPMailer remains a practical standalone option because it handles SMTP authentication, attachments, multipart messages, UTF-8, and header-injection protection. Symfony Mailer is a strong fit when you are already inside Symfony or using its components. Whichever library you pick, the pattern is the same: 1. Compose the message through a mailer. 2. Send through authenticated SMTP or a transactional email provider. 3. Keep credentials in environment configuration. 4. Log provider errors. 5. Test locally with a catcher instead of sending real messages. Install PHPMailer with `composer require phpmailer/phpmailer`. Load Composer's `vendor/autoload.php` before using its classes, and configure the SMTP environment variables on your server. See the [Composer guide](/programming/php/dependencies/composer/) for dependency setup. ```php isSMTP(); $mail->Host = $_ENV["SMTP_HOST"]; $mail->SMTPAuth = true; $mail->Username = $_ENV["SMTP_USER"]; $mail->Password = $_ENV["SMTP_PASSWORD"]; $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; $mail->Port = 587; $mail->setFrom("hello@example.com", "CodeWalkers"); $mail->addAddress("reader@example.com"); $mail->Subject = "Your export is ready"; $mail->Body = "Download your export from the dashboard."; $mail->send(); ``` Use the host, port, and encryption settings supplied by your SMTP provider. The example uses STARTTLS on port 587; do not disable certificate checks to work around connection errors. PHPMailer is still the simple standalone choice when the application is plain PHP, a small legacy app, or a framework that does not already own email. Symfony Mailer is usually a better fit when the app already has a container, environment-driven configuration, and a queue layer. Both can send mail independently of a framework; choose the library that fits your existing application. To use Symfony Mailer separately, run `composer require symfony/mailer` and load `vendor/autoload.php`. Set MAILER_DSN to the connection string your provider requires. The transport can then change without rewriting the message. ```php from("hello@example.com") ->to("reader@example.com") ->subject("Your export is ready") ->text("Download your export from the dashboard.") ->html("

    Download your export from the dashboard.

    "); $mailer->send($email); ``` Keep the decision to send a message separate from the code that connects to the email provider. ## Keep Email Setup in One Place Create a fresh configured mailer through one factory or service for each message, rather than scattering setup through controllers, form handlers, and cron scripts. Put the transport setup in one factory or service, then keep each message as a small named operation. The code that approves a comment should not know about SMTP ports. The code that sends the message should not decide whether the comment was valid. ```php clearAllRecipients(); $mail->clearAttachments(); $mail->addAddress($recipient); $mail->Subject = "Your export is ready"; $mail->Body = "Download it here: {$downloadUrl}"; $mail->send(); } ``` That shape is easier to test, easier to replace with a provider API later, and easier to rate-limit. It also keeps sensitive SMTP configuration away from ordinary request code. Pass a fresh, configured PHPMailer instance for each message. Reusing an instance can retain recipients, attachments, reply addresses, HTML mode, or alternative text from an earlier email. Keeping email setup in one place makes an old PHP app easier to maintain. A single `MailerFactory` plus named message functions provides one place for logging, a fake transport, a local development catcher, and provider-specific retry behavior. A dozen scattered `mail()` calls creates a search project every time the SMTP password changes. Do not log the full message body by default. Log the message type, recipient domain, provider message id when you have one, success or failure, and enough context to find the related row. For password resets, invoices, exports, and account notifications, the logs should tell you what happened without leaking secrets or personal data into the log store. ## How Do You Configure SMTP Authentication in PHP? Configure SMTP authentication in PHP by setting the provider’s host, port, username, password or app password, and encryption mode on your mailer. Some providers also support OAuth-based SMTP, and some teams skip SMTP entirely in favor of a transactional email API, with Postmark, Amazon SES, and Resend as neutral examples. The PHP code should hide that choice behind configuration. Whichever library you choose, configure credentials in one place and decide how to record and retry failed sends. Treat the `From` address as a domain decision. If the message claims to come from `example.com`, the sending provider, DNS records, and bounce handling all need to line up with that domain. Use `Reply-To` for replies, and use the provider's envelope sender or return-path setup for delivery and bounce processing instead of improvising headers in PHP. ## How Do You Attach Files to PHP Emails? Use the mailer library to attach files to PHP emails. It handles MIME encoding, but your application must still check which file can be sent and how large it is. Before attaching a file: - Generate or validate the filename you expose to the recipient. - Check file size before loading it into memory. - Store private files outside the web root where possible. - Use a mailer API for MIME encoding work. - Avoid attaching user-uploaded files unless the workflow really needs it. For many applications, the better design is a signed download link with an expiry time. The email stays small, and the app keeps control over access. If you do attach a file, let the mailer read and encode it. PHPMailer's `addAttachment()` and Symfony's attachment APIs exist so application code does not have to assemble multipart boundaries by hand. Check the file path before attaching it, use a safe display name, and keep user-controlled filenames out of the headers unless they have been normalized. ## Why Should PHP Emails Include Plain Text? PHP emails should include a plain-text alternative so the message remains readable when a client cannot display HTML. ```php isHTML(true); $mail->Subject = "Reset your password"; $mail->Body = "

    Use this link to reset your password.

    "; $mail->AltBody = "Use this link to reset your password."; ``` PHPMailer creates the multipart message from `Body` and `AltBody`. You do not need to write the MIME separators yourself. Keep transactional templates small at first, especially when the message carries an account or security action. A transactional message needs a clear subject, a recognizable sender, the one action the recipient should take, and enough plain text to make sense in a security product or terminal mail client. CSS-heavy newsletter habits are usually the wrong model for a password reset or export notification. ## Why Might PHP Emails Land in Spam? PHP emails can land in spam because of domain authentication, sender reputation, or message content. Set up SPF, DKIM, and DMARC with your provider, then monitor bounces and complaints. These settings belong to your domain and email provider, rather than your PHP script. DMARC checks whether a domain authenticated by SPF or DKIM aligns with the domain in the visible From address. A message can be valid PHP output and still fail this check. For a small application, start with one verified sending domain and one provider. Avoid sending product mail from a personal inbox, a random server hostname, or a domain that has no authentication records. Those shortcuts work just long enough to make debugging miserable later. ## Avoid Accidental Bulk Mail Once email works, the next temptation is a loop. A CSV import, admin screen, or database query can turn one message into thousands. That needs a queue or batch process, not a browser request that tries to send everything before the page times out. At minimum, separate the selection of recipients from the sending step: 1. Validate the input that decides who receives mail. 2. Store a pending message or job. 3. Send in controlled batches. 4. Record success and failure per recipient. 5. Retry only failures that are safe to retry. Local development deserves the same caution as production. Use a mail catcher, fake transport, or test provider mode so a form test does not send real messages to users. The first test should show that the app created the right message for the right recipient. ## Common Pitfalls & Debugging ### Mail() Returns True but the Message Never Arrives Symptom: PHP reports success, but the inbox stays empty. Cause: `mail()` reports that the local system accepted the message, not that a receiving server delivered it. Fix: inspect the transport or provider event, then check domain authentication, bounces, suppression lists, and spam placement. ### User Input Reaches a Mail Header Symptom: a recipient, subject, or reply address creates extra headers or malformed mail. Cause: untrusted text was concatenated into a header. Fix: validate addresses, reject line breaks in header values, and pass values through a maintained mailer's methods. ### One Action Sends the Message Twice Symptom: a retry or double form submission sends duplicate account mail. Cause: the request sends before recording a unique message job. Fix: create an idempotent job or outbox record first, then let one worker own each send attempt. ## Frequently Asked Questions ### Should PHP use mail() or authenticated SMTP? PHP applications should usually send account and customer email through a maintained mailer with authenticated SMTP or a transactional provider. The mail() function can suit a simple internal tool on a server whose mail service you control. ### Can PHP mailers send attachments? Maintained PHP mailers can send attachments through dedicated APIs. PHPMailer, Symfony Mailer, and framework mailers handle the MIME encoding. Validate the path, size, media type, and display name before attaching a file, especially when any value came from a user. ### Why should PHP emails include both HTML and plain text? PHP emails should include a plain-text version so recipients can read the message when HTML is unavailable or blocked. Both versions should contain the same message and links. ### Should PHP emails attach large files or link to them? PHP emails should generally link to large files rather than attach them. For private files, use an expiring link and require authorization when appropriate. This keeps the email small and lets the application control access. ### What is the actual difference between SPF and DKIM? SPF is a DNS record listing which mail servers are allowed to send for a domain, checked when a message arrives. DKIM adds a cryptographic signature to outgoing mail so the receiver can verify the message was not altered after it was signed. ### Should transactional email like password resets share the same sending domain as marketing email? Transactional emails and marketing emails are often better sent through separate subdomains and sending streams. Marketing complaints can affect delivery reputation, so separating the traffic helps protect important messages such as password resets. ### Can one email include both an HTML body and file attachments? An email can contain HTML, a plain-text alternative, and file attachments together. PHPMailer provides isHTML(), AltBody, and addAttachment() to assemble those parts without writing MIME formatting by hand. ## Self-Check Predict the output from this transport result helper: ```php Php [PHP](/programming/php/) reads small text files with `file_get_contents()` and writes them with `file_put_contents()`. For a large log, read one line at a time so the whole file does not have to fit in memory. File I/O is one of the first practical things people want from PHP: read a log, write a counter, update a flat-file guestbook, or parse a simple text export before a database enters the picture. The need has not changed, and trusting a filename that arrived from a form is still the habit to drop. Let your application choose which files it can open. A visitor can select a document from an approved list, but should not be able to submit any path on your server. ## Guide Path This is the first files-and-data tutorial in the PHP path. Read it before [processing XML with PHP](/programming/php/xml/processing-xml/) or [streaming media with PHP](/programming/php/media/streaming-media/), because both depend on safe path handling, predictable reads, and clear storage boundaries. ## How Do You Read a Text File in PHP? Read a small text file in PHP with `file_get_contents()`, which returns its contents as a string: ```php format(DateTimeInterface::ATOM), "import finished", ); $bytes = file_put_contents( __DIR__ . "/../storage/import.log", $entry, FILE_APPEND | LOCK_EX, ); if ($bytes === false) { throw new RuntimeException("Could not append to import log."); } ``` This is fine for modest logs and audit trails. If the file is hot, shared across processes, or business-critical, stop treating a text file as infrastructure and put the event into a database or a real log system. If the file is protected media rather than a log or export, the shape changes. Use the [streaming media with PHP](/programming/php/media/streaming-media/) guide for the boundary between PHP authorization and web-server file delivery. ## Which PHP File Modes Erase Existing Contents? The PHP `fopen()` manual states that the `"w"` family truncates an existing file to zero length when it opens. ```php "terms.txt", "privacy" => "privacy.txt", ]; $key = $_GET["file"] ?? "terms"; $filename = is_string($key) ? ($allowedFiles[$key] ?? $allowedFiles["terms"]) : $allowedFiles["terms"]; $base = realpath(__DIR__ . "/../storage"); if ($base === false) { throw new RuntimeException("Storage directory is missing."); } $path = realpath($base . "/" . $filename); if ($path === false || !str_starts_with($path, $base . DIRECTORY_SEPARATOR)) { throw new RuntimeException("Invalid file path."); } $contents = file_get_contents($path); if ($contents === false) { throw new RuntimeException("Could not read file."); } echo htmlspecialchars($contents, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); ``` The user chooses a known key while the server chooses the actual filename. An unrecognised key falls back to the terms file. ## ZIP Downloads The old ZIP File Maker snippet answered a real need: take a few server files and hand the user one download. In current PHP, reach for `ZipArchive` and keep the same path discipline you used for reads. ```php "terms.txt", "privacy" => "privacy.txt", "readme" => "readme.txt", ]; $base = realpath(__DIR__ . "/../storage/public-docs"); if ($base === false) { throw new RuntimeException("Document directory is missing."); } $paths = []; foreach ($fileKeys as $key) { if (!is_string($key) || !isset($allowedFiles[$key])) { throw new InvalidArgumentException("Unknown document key."); } $path = realpath($base . "/" . $allowedFiles[$key]); if ($path === false || !is_file($path) || !str_starts_with($path, $base . DIRECTORY_SEPARATOR)) { throw new RuntimeException("Document is unavailable."); } $paths[$path] = basename($path); } if ($paths === []) { throw new InvalidArgumentException("Choose at least one document."); } $zipPath = tempnam(sys_get_temp_dir(), "cw-"); if ($zipPath === false) { throw new RuntimeException("Could not create temporary file."); } $zip = new ZipArchive(); $opened = false; try { if ($zip->open($zipPath, ZipArchive::OVERWRITE) !== true) { throw new RuntimeException("Could not open ZIP archive."); } $opened = true; foreach ($paths as $path => $name) { if (!$zip->addFile($path, $name)) { throw new RuntimeException("Could not add document."); } } $closed = $zip->close(); $opened = false; if (!$closed) { throw new RuntimeException("Could not finish ZIP archive."); } return $zipPath; } catch (Throwable $error) { if ($opened) { $zip->close(); } if (is_file($zipPath)) { unlink($zipPath); } throw $error; } } ``` Do not pass arbitrary submitted paths into the archive. The user can choose a known document key, but the server still chooses the path and archive name. After sending the ZIP, delete the temporary file so the download directory does not become a quiet storage system. ## Directory Listing A directory listing is another good example of useful file code that needs a strict boundary. You may want an admin page that lists uploads, exports, or generated reports. That does not mean the browser should choose the directory. ```php isFile()) { continue; } $files[] = [ "name" => $entry->getFilename(), "size" => $entry->getSize(), "updated_at" => $entry->getMTime(), ]; } usort($files, fn ($a, $b) => $b["updated_at"] <=> $a["updated_at"]); return $files; } ``` Render those filenames with `htmlspecialchars()` the same way you render any other value. File names are not safe just because the server produced them; they may still include characters that mean something in HTML. If users can upload files, store a server-owned name and keep the original display name as metadata. ## When Should PHP Use a Database Instead of Files? Use a database when PHP needs to search related records, coordinate concurrent updates, or make several changes together. Text files remain useful for simpler jobs: - Small config snapshots. - Logs that another tool will collect. - Imports and exports. - Static text fragments. - Small ZIP downloads made from server-approved files. - One-off scripts where the file is the point. They are a poor fit once the data starts behaving like application state: - User accounts. - Comments, posts, orders, and messages. - Anything that needs joins. - Anything that needs reliable concurrent writes. - Anything you expect to search, paginate, or report on later. That is where the [small PHP database app](/programming/php/mysql/dynamic-sites/) shape becomes cleaner. If the file is a spreadsheet export, use the [CSV-to-SQL import guide](/programming/sql/imports/csv-to-sql/) before the rows touch final tables. A database is easier to manage when you need to filter rows, relate records, or coordinate updates. ## Common Pitfalls & Debugging ### A Read Failure Looks Like Empty Content Symptom: a missing or unreadable file is treated as an empty document. Cause: the code uses a loose check, so `false` and an empty string are handled alike. Fix: compare the result with `=== false`, then report or throw the real read error. ### Two Writers Lose an Update Symptom: a counter or small state file occasionally loses a change. Cause: each request reads before taking the exclusive lock, so both calculate from the same old value. Fix: open the file, lock it, then perform the complete read-modify-write sequence before releasing the lock. ### A Resolved Path Leaves the Storage Root Symptom: a request can read a neighboring directory with a similar prefix. Cause: the path check compares text prefixes without a directory boundary. Fix: resolve both paths and require the file path to begin with the resolved base plus `DIRECTORY_SEPARATOR`. ## Frequently Asked Questions ### Can PHP file_get_contents() read a URL? PHP file_get_contents() can fetch an HTTP or HTTPS URL when allow_url_fopen is enabled. Stream context options let you set headers and a read timeout. An HTTP client library can make response handling and retries easier for API requests. ### Does file_put_contents() create a missing file? PHP file_put_contents() creates the file when the parent directory exists and PHP has permission to write there. It returns false on failure, so compare the result strictly and handle that case instead of assuming the write succeeded. ### Which fopen() mode appends without truncating? PHP fopen() modes a and ab open a file for writing at the end without truncating its existing contents. The a+ and a+b modes also permit reading. Add the b flag for consistent binary-mode behavior across platforms. ### Can you query a CSV file directly with SQL? CSV files can be queried with SQL through features such as PostgreSQL file_fdw and SQLite’s CSV virtual table. Indexing and performance stay far more limited than an imported table. ### Can PHP send a ZIP archive to the browser without writing it to disk first? PHP can send a ZIP archive without first saving it by using a library designed for streaming ZIP files. ZipArchive uses a filesystem path, so a ZipArchive download normally requires a temporary file that you delete after sending it. ## Choose the Right PHP File Function Use `file_get_contents()` and `file_put_contents()` for small, clear jobs. Use streams or `SplFileObject` for large files. Lock the complete update when more than one request might write, and let application code own every path. For records you need to query or update together, the [PHP and MySQL with PDO](/programming/php/mysql/pdo/) guide is the next step. --- ## PHP Email Validation: Filters and Verification URL: /programming/php/forms/email-validation/ Cluster: Programming > Php To validate an email address in [PHP](/programming/php/), pass it to `filter_var()` with `FILTER_VALIDATE_EMAIL`. The filter checks the address format. It does not tell you whether the mailbox exists or belongs to the person submitting the form. For an account signup, send a confirmation link as well. For a mailing list, ask for consent separately. A correctly formatted address does not establish either ownership or permission to send marketing email. For the rest of a registration form, see [form and spelling validation in PHP](/programming/php/forms/spelling-validation/) after this. If the accepted address needs a message, continue to [sending email in PHP](/programming/php/email/sending-email/). ## How Do You Validate an Email Address in PHP? Validate an email address in PHP with filter_var() and FILTER_VALIDATE_EMAIL after trimming surrounding whitespace. Reject an empty value before checking its syntax: ```php prepare( "INSERT INTO newsletter_signups (email, created_at) VALUES (:email, CURRENT_TIMESTAMP)", ); $statement->execute(["email" => $email]); } ``` If the address must be unique, add a database constraint. This example uses PostgreSQL syntax: ```sql CREATE TABLE newsletter_signups ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, email TEXT NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ); ``` The database constraint also prevents two simultaneous signup requests from inserting the same address. Handle a duplicate-address error in your form response. ## How Do You Confirm That a User Owns an Email Address? Confirm access to an email address by sending a unique, expiring link and requiring the recipient to follow it. Syntax validation alone cannot establish who receives the messages: ```php Php [PHP form validation](/programming/php/) checks whether submitted values meet your rules before you save them. An email needs a valid format, a display name needs a sensible length, and a password must meet your account requirements. HTML can catch simple mistakes before submission, but PHP must check the values again. Database constraints prevent invalid records, and output escaping keeps submitted text from becoming HTML. Spellchecking is optional help for the writer, not a security check. This sits in the intermediate PHP tutorial track because the code crosses HTML, server-side branching, sessions, SQL constraints, and output escaping. If the server-side rules themselves are still fuzzy, read [PHP conditionals and control flow](/programming/php/control-flow/) and [PHP email validation](/programming/php/forms/email-validation/) first. Most validation bugs are ordinary branches with the wrong comparison or a missing failure path. ## What Should PHP Form Validation Check? PHP form validation checks required fields, data types, formats, and lengths. Other protections have separate jobs: 1. Browser hints help the user correct obvious mistakes. 2. Server validation decides whether the request is acceptable. 3. CSRF protection checks a submitted token against the session token to help prevent cross-site request forgery. 4. SQL constraints enforce facts that must remain true after the PHP request is gone. 5. Output escaping makes accepted text safe in the output context where it appears. Those jobs are related, but they are not interchangeable. `required` on an HTML field does not protect the server. A database `UNIQUE` constraint does not give the user a friendly form error by itself. `htmlspecialchars()` does not prove the email address is syntactically valid. A CSRF token does not make the submitted title a good title. ## Can HTML Replace PHP Form Validation? HTML constraints cannot replace PHP validation because anyone can send a request without using your form. Attributes such as required and minlength still give people useful feedback before submission: ```html
    ``` MDN is explicit that client-side validation is a usability feature. The server still has to repeat the rules because the browser is not the authority. Anyone can send a POST request without your form. Use the HTML layer for fast feedback. Use PHP for the decision that matters. ## How Do You Validate Form Fields in PHP? PHP validates form fields by checking their types first, then applying the rules for each field. This example requires the mbstring extension and returns errors alongside the accepted values. ```php , 1: array} */ function validateRegistration(array $input): array { $email = is_string($input["email"] ?? null) ? trim($input["email"]) : ""; $displayName = is_string($input["display_name"] ?? null) ? trim($input["display_name"]) : ""; $password = is_string($input["password"] ?? null) ? $input["password"] : ""; $errors = []; if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) { $errors["email"] = "Enter a valid email address."; } $displayNameLength = mb_strlen($displayName, "UTF-8"); if ($displayNameLength < 2 || $displayNameLength > 60) { $errors["display_name"] = "Use 2 to 60 characters."; } if (mb_strlen($password, "UTF-8") < 12 || strlen($password) > 72) { $errors["password"] = "Use at least 12 characters and no more than 72 bytes."; } return [ $errors, [ "email" => $email, "display_name" => $displayName, "password" => $password, ], ]; } ``` PHP's `FILTER_VALIDATE_EMAIL` is a syntax check, not a deliverability guarantee. It tells you whether the string looks like an email address. It does not prove the mailbox exists, that the user owns it, or that mail will arrive. Confirmation emails and bounce handling are different layers. `mb_strlen()` counts Unicode characters rather than bytes. This example also rejects passwords above 72 bytes because it uses bcrypt below, which otherwise truncates longer input. Store the hash, never the password itself. ## How Should PHP Display Form Errors? A PHP form handler should redisplay invalid fields with specific error messages and save data only when validation passes. The abbreviated handler below assumes your application provides the database connection, CSRF helper, and form view; the form also needs a hidden CSRF token. ```php "> ``` That is output escaping rather than validation, because the user may be allowed to have an apostrophe or angle bracket in a field depending on the application. The question is how that value is represented safely in HTML. If a field intentionally accepts HTML, do not treat `htmlspecialchars()` as a full content policy. Use a real sanitizer with a strict allowlist, and be honest about the risk. ## Should PHP Forms Check Spelling? PHP forms can offer spelling suggestions for comments and other prose, but spelling mistakes should not normally block submission. Browser spellchecking is enough for many forms. Server-side Pspell remains available separately through PECL after its removal from the PHP bundle in 8.4. For ordinary text fields, use the browser's spelling support: ```html ``` The browser may underline likely spelling mistakes without blocking submission. Support varies, and enhanced spellchecking may send text to a third-party service. Disable spellchecking for sensitive text. If your product needs server-side spellchecking, treat it as an editorial workflow. Run it after the text is saved as a draft, show suggestions as suggestions, and let a human decide. Do not reject a name, product code, domain, username, or medical term because a dictionary disagrees with it. Older validation scripts loved regular expressions and word lists because they gave the appearance of certainty. Real user data is messier, so enforce only the rules the application needs. ## Save Validated Values with Prepared Statements Once validation passes, the insert still uses prepared statements: ```php prepare( "INSERT INTO users (email, display_name, password_hash) VALUES (:email, :display_name, :password_hash)", ); $statement->execute([ "email" => $email, "display_name" => $displayName, "password_hash" => password_hash($password, PASSWORD_BCRYPT), ]); } ``` Prepared statements keep submitted values separate from SQL instructions. Validation checks whether those values are acceptable; neither replaces the other. For a fuller database-backed shape, read [PHP and MySQL with PDO](/programming/php/mysql/pdo/) and [building a small PHP database app](/programming/php/mysql/dynamic-sites/). If the same input arrives as JSON instead of a browser form, the validation principle is unchanged; the [PHP API guide](/programming/php/apis/custom-api/) covers that boundary. When the accepted form data triggers a notification, continue to [sending email in PHP](/programming/php/email/sending-email/). ## Check Input Before Saving It Check form values in PHP even when HTML has already checked them. Return clear errors, save accepted values through prepared statements, and escape text when displaying it. Keep spelling suggestions optional. ## Frequently Asked Questions ### How should you validate an email address in PHP? PHP checks email syntax with filter_var() and FILTER_VALIDATE_EMAIL. A valid format does not prove that the address receives mail. Send a confirmation link to check that the person can access the mailbox. ### Should PHP trim form input before validation? PHP should trim form input when leading or trailing spaces have no meaning, such as in an email address. Trim before checking length. Do not trim passwords because spaces may be part of the password. ### How does PHP validate a date submitted by a form? PHP validates a submitted date by parsing the expected format and checking that the result matches the original value. That rejects dates such as February 31, which a parser may otherwise move into the next month. ### Should PHP form errors name the field that failed? PHP form errors should name the affected field and explain how to fix it. Keep non-sensitive values in the form and place each message beside its field. Never refill a password field. ### Does every text field in a PHP form need spellchecking? Not every text field in a PHP form needs spellchecking. Enable it for prose such as comments, and disable it for usernames, codes, and reference numbers. Browser defaults and support vary. --- ## PHP Frameworks Compared URL: /programming/php/frameworks/ Cluster: Programming > Php [PHP frameworks](/programming/php/) provide reusable tools for building web applications. Full-stack frameworks include features such as validation, templates and database access. Microframeworks focus on fewer jobs, usually routing and middleware, and let you choose the rest. Laravel is one common starting point for full web applications. Symfony fits long-lived systems that need explicit boundaries and reusable components. CakePHP suits convention-heavy CRUD, while Slim suits small APIs and integration services. Fat-Free Framework, usually called F3, stays closer to plain PHP for small applications. ## Quick Comparison | Framework | Best fit | Tradeoff | | Laravel | Full-stack products and teams | Many features and conventions to learn | | Symfony | Long-lived systems and components | More initial configuration | | CakePHP | Convention-heavy CRUD applications | Less flexible application shape | | Slim | APIs and integration services | Supporting libraries require setup | | Fat-Free Framework | Small applications with little setup | Smaller package ecosystem | Check how much structure the application can use before framework conventions become extra work. ## How Should You Choose a PHP Framework? Choose a PHP framework by the features your application needs, your team’s experience and the amount of setup you want to maintain. A full-stack framework bundles common features; a microframework lets you assemble them yourself. Match the framework to the application: - If the app is a product with users, billing, jobs, notifications, admin screens, and a roadmap, start with Laravel.- If the app is a long-lived system that needs clear service boundaries, shared components, and team discipline, start with Symfony.- If the app is mostly forms, tables, validation, and predictable database-backed CRUD, CakePHP deserves a real look.- If the app is an API edge, webhook receiver, small integration service, or prototype, Slim is enough framework.- If the app is a small legacy-friendly rebuild and staying close to plain PHP is the point, Fat-Free Framework can still make sense. Frameworks bundle trade-offs, so pick the one that keeps the application's repeated decisions visible and consistent. ## Laravel Best for: Full web applications with common features built in. Laravel provides an application framework instead of a loose set of components. The Laravel 13 documentation covers starter kits, Eloquent, migrations, queues, scheduled jobs, testing, deployment, Laravel Cloud, and Laravel Boost for agent-assisted development. Laravel gives ordinary application decisions a documented home: routes, migrations, jobs, policies, and validation each have known locations. Those conventions also give AI coding agents less blank space to fill with generic PHP guesses. Pick Laravel when the application needs a full application stack: - The project is a product with ongoing application needs.- You want built-in support for recurring web tasks.- You expect future developers or AI coding agents to understand the project layout quickly.- You are happy with conventions around models, migrations, controllers, queues, and tests. The smallest useful Laravel route is readable enough: ```php true]; }); ``` The application shape around this small route carries the cost. Laravel makes sense when you want migrations for schema history, Eloquent for model relationships, queues for background work, and a known testing story before the first production bug arrives. For a few routes and a form, Laravel may add setup you do not need. It can still be a sensible choice if your team already knows it or the application is likely to grow. ## Symfony Best for: Applications needing flexible architecture and reusable components. Symfony fits systems that need strong boundaries and a longer planning horizon. Its documentation is organized around request and response architecture, routing, controllers, services, dependency injection, events, security, forms, cache, and deployment. Symfony also publishes standalone components that can be used inside other PHP projects. That component story matters because Symfony is both an app framework and a set of maintained building blocks used across the PHP ecosystem. The full framework gives you a coherent architecture. The components let you borrow only what you need when adopting the whole stack would be too much. Pick Symfony when the architecture needs to be explicit: - The app needs explicit architecture.- The team is comfortable with dependency injection and services.- Long-term maintainability matters more than getting a demo up tonight.- You may want standalone components without adopting the whole framework. A minimal controller shows the tone Symfony usually takes: ```php connect("/status", ["controller" => "Pages", "action" => "status"]); }; ``` That route expects a controller action, and the controller expects CakePHP's normal application organization. A back-office tool with accounts, records, exports, and admin forms can use those ready-made rooms. A highly customized API may need more changes to those conventions. CakePHP is a weaker fit for tiny APIs or highly custom application shapes. Its conventions suit the routine middle of database-backed web software. ## Slim Best for: Small APIs where you choose the supporting libraries. Slim calls itself a PHP micro framework, and that is the correct mental model. It gives you routing, middleware, PSR-7 request/response handling, and enough structure to build small web apps and APIs without adopting a full application framework. Slim is the right answer when the HTTP layer is the main thing you need. You bring the container, database layer, validation, templates, and application conventions you want. Its own documentation describes Slim as a dispatcher: receive an HTTP request, invoke the matching callback, return an HTTP response. That is exactly the right level of ambition for a lot of API work. Pick Slim when the HTTP layer is the main surface area: - You are building an API.- You want PSR-style request and middleware handling.- You already know which libraries you want around it.- A full-stack framework would hide more than it helps. A small Slim route keeps the framework machinery limited: ```php get("/status", function (Request $request, Response $response): Response { $response->getBody()->write("ok"); return $response; }); $app->run(); ``` Slim requires more setup because you choose and connect the supporting packages. That suits teams that already know which ORM, validator, template layer, logger, and configuration approach they want. Teams expecting the framework to make those decisions need more structure. ## Fat-Free Framework Best for: Small applications that stay close to plain PHP. Fat-Free Framework, often called F3, is the lightweight holdover worth a current look. The current Fat-Free 3.9 documentation describes a micro-framework with a small code base, routing, cache, multilingual support, and SQL and NoSQL database helpers. F3 aims to keep the request path visible while providing common web-application tools. Pick Fat-Free when keeping the project small is the point: - You want a small PHP framework with routing and helpers.- You are rebuilding or maintaining a legacy-friendly PHP app.- You do not want Laravel's project shape.- You value low ceremony over mainstream ecosystem size. The basic F3 route shape is intentionally plain enough to read at a glance: ```php route("GET /", function () { echo "Hello from a small PHP app"; }); $f3->run(); ``` The important thing is what is missing from that route. There is no generated application skeleton between the request and the idea. For a tiny app, that can feel good because the code stays close to the request. For a larger system, the same freedom can turn into inconsistent structure unless the team agrees on its own conventions. F3 can also be an incremental step for older PHP code. If an old application is a pile of includes and globals, moving straight to a full framework can turn the migration into a second product. F3 can give that code a front controller, routes, templates, and a cleaner database layer first. Database work still needs the same discipline as any PHP app. F3 has helpers, but user input should not end up in raw SQL strings. If the app stores anything important, read [PHP and MySQL with PDO](/programming/php/mysql/pdo/) before wiring forms into framework routes. F3 has a smaller set of official packages and documentation than the full-stack frameworks in this comparison. Choose it when the application is small and that smallness should remain visible. ## Quick Starts at a Glance Each quick start shows the first application shape the framework provides. ```bash # Laravel, full application skeleton composer global require laravel/installer laravel new example-app cd example-app php artisan serve ``` ```bash # Symfony, full web app skeleton composer create-project symfony/skeleton example-app cd example-app composer require webapp php -S localhost:8000 -t public/ ``` ```bash # CakePHP, convention-first application skeleton composer create-project --prefer-dist cakephp/app example-app cd example-app bin/cake server ``` ```bash # Slim, small app plus PSR-7 implementation composer require slim/slim slim/psr7 ``` ```bash # Fat-Free Framework, small core package composer require bcosca/fatfree-core ``` Laravel, Symfony, and CakePHP create an application skeleton. Slim and F3 start closer to a library that you shape into an application. The quick start reveals how much structure arrives before the first route. All five quick starts run Composer, PHP's dependency manager. The [Composer guide](/programming/php/dependencies/composer/) explains the `composer.json` and `composer.lock` files a project ends up with, and how to update them safely. ## How Does Database Work Affect Your PHP Framework Choice? Choose a PHP framework whose database tools suit your tables, relationships and queries. Check how it handles migrations, transactions and complex queries before committing to its ORM. Laravel's Eloquent is productive when the application model fits active-record style relationships. It works especially well when the team accepts Laravel's migration and model conventions. Symfony can use Doctrine or a different persistence layer, which gives you more architecture control but more decisions to make. CakePHP's ORM fits its convention-first model and is comfortable for CRUD-heavy applications. Slim and F3 leave more of the data-access shape in your hands, which can be good or reckless depending on the team. If the app is mostly database-backed forms, choose from the table relationships before comparing route syntax. Sketch the tables and relationships, then ask how each framework represents that model. A framework adds too little value when attractive routes still leave queries scattered through callbacks. For plain PHP database work, [PHP and MySQL with PDO](/programming/php/mysql/pdo/) is still the baseline. For relationship design, read [SQL schema design](/programming/sql/schema-design/) before falling in love with any framework's model layer. ## When Is Plain PHP Enough? Plain PHP can be enough for a small internal tool or a single-purpose endpoint. A small internal tool, one webhook receiver, or a throwaway migration helper may be better as a plain PHP script behind a locked-down route. For a small project, plain PHP can be deliberate scope control. The line is crossed when the app starts repeating decisions about routing, validation, authentication, templates, migrations, error handling, tests, or deployment. A framework then records the chosen application shape so each new file does not invent it again. ## Common Pitfalls & Debugging ### The Framework Is Larger than the Application A three-route service needs many generated files and configuration layers. A full-stack framework was chosen for a small HTTP boundary. Remove the unused layers or use Slim, F3, or plain PHP while the scope remains small. ### The Team Fights the Conventions Controllers, models, and database code live outside the locations the framework expects. The chosen conventions do not match the team's application shape. Follow the framework consistently or choose one whose defaults match the project. ### Upgrades Have No Owner The project cannot update because deprecated packages and copied framework internals have accumulated. The initial choice ignored release and upgrade work. Assign upgrade ownership, follow official release guidance, and avoid replacing extension points with copied internals. ## Frequently Asked Questions ### Which PHP framework should a beginner learn first? Laravel gives a beginner building a full web product documented conventions for routing, validation, database work, queues, and testing. Learn plain PHP classes, HTTP, and PDO first so the framework's abstractions remain understandable. ### Is Symfony better than Laravel for large applications? Symfony fits systems that need explicit service boundaries, reusable components, and deliberate architecture. Laravel also supports large applications, but it supplies more application conventions by default. The better fit depends on whether the team wants prescribed product structure or more architectural control. ### Can an existing plain PHP app move to a framework gradually? An existing PHP application can usually adopt a framework gradually. One approach puts the framework's front controller in charge of new paths while the old scripts keep serving the rest, so routes migrate one at a time instead of in a single cutover. ### Does picking a framework lock you into its template engine? A PHP framework does not necessarily lock an application into one template engine. Laravel defaults to Blade and Symfony to Twig, but each can render the other with extra setup, and an application serving JSON only can skip framework-rendered HTML altogether. ### Can Laravel's Eloquent ORM be used outside a full Laravel application? Eloquent can be used without Laravel. It is published as a standalone package that a non-Laravel PHP project can install directly, though it loses some conveniences that only exist inside Laravel's own service container and configuration. ### Does Slim include a database layer or ORM of its own? Slim does not include a database layer. It is a routing and middleware micro-framework with nothing bundled for data access, so a project adds PDO directly, Eloquent, Doctrine, or another library of its own choosing. ## Self-Check 1. Which project best matches Slim: a billing product, a small webhook API, or a convention-heavy admin system? 2. What does the Slim `/status` route write to the response body? 3. Which framework publishes components that can be used without its full application stack: Laravel, Symfony, or CakePHP? 4. What repeated decisions signal that plain PHP has outgrown its current structure? 5. What does the Fat-Free Framework `GET /` route print? ### Answers 1. A small webhook API. Slim supplies routing and middleware while leaving the surrounding stack to the project. 2. `ok`. The callback writes that string into the PSR-7 response body before returning it. 3. Symfony. Its Console, HttpFoundation, DependencyInjection, and other components can be adopted separately. 4. Repeated application rules. Shared routing, validation, authentication, migrations, error handling, tests, or deployment patterns justify recorded structure. 5. `Hello from a small PHP app`. The route callback echoes that text before F3 completes the response. ## Recommendation and Next Steps Laravel fits full web products that can use its conventions. Symfony fits systems that need stronger architectural boundaries, Slim fits small APIs, CakePHP fits convention-heavy CRUD, and F3 fits applications that must stay close to plain PHP. If the application will spend most of its life talking to a database, read [PHP and MySQL with PDO](/programming/php/mysql/pdo/) next, then work through [SQL joins](/programming/sql/joins/) and [schema design](/programming/sql/schema-design/). Framework choice helps, but the data model decides whether the app ages well. --- ## PHP Recursion: Functions, Trees, and Base Cases URL: /programming/php/functions/recursion/ Cluster: Programming > Php Recursion lets a [PHP function](/programming/php/) call itself with a smaller version of the same problem until a base case returns without another call. It fits category trees, nested comments, directory structures, and other data that repeats its own shape. Each recursive call pauses the previous one. Once the base case returns, those calls finish in reverse order. Too many unfinished calls can exhaust memory, so deeply nested input needs a limit. Use recursion for naturally nested data with known depth. Use iteration with an explicit stack or queue when depth can be large or unbounded. ## Guide Path Read this after [PHP OOP for beginners](/programming/php/oop/) if your data has nested categories, replies, menus, or trees. It also pairs well with [PHP and MySQL dynamic sites](/programming/php/mysql/dynamic-sites/) because nested categories and comments often start as rows with a parent ID. ## How Does a PHP Recursive Function Stop? A PHP recursive function stops when its base case returns without another recursive call. Put that condition before the recursive call. This countdown returns as soon as the number reaches zero or below, so every later call has a visible destination. ```php "Programming", "children" => [ ["label" => "PHP", "children" => []], ["label" => "SQL", "children" => []], ], ], [ "label" => "AI Coding Tools", "children" => [], ], ]; function flattenMenu(array $items, int $depth = 0): array { $rows = []; foreach ($items as $item) { $rows[] = [ "label" => $item["label"], "depth" => $depth, ]; if (($item["children"] ?? []) !== []) { $rows = [ ...$rows, ...flattenMenu($item["children"], $depth + 1), ]; } } return $rows; } $flatMenu = flattenMenu($menu); ``` The sample produces this label and depth sequence: ```text Programming: 0 PHP: 1 SQL: 1 AI Coding Tools: 0 ``` The function follows the same shape as the data. Each menu item can have children, and each child can have children of the same shape. Recursion keeps the code close to that structure. PHP arrays make this example convenient because an array can represent a list, a map, a stack, a queue, or a nested structure. That flexibility is useful, but it also hides mistakes. A missing `children` key, a string where an array should be, or a row that points to itself can turn a clean traversal into a fragile one. ## How Do You Build a Tree from Database Rows in PHP? Build a tree in PHP by grouping database rows by parent ID, then recursively adding each parent’s children. Fetch the rows before traversing them so each recursive call does not run another query. ```php 1, "parent_id" => null, "title" => "Programming"], ["id" => 2, "parent_id" => 1, "title" => "PHP"], ["id" => 3, "parent_id" => 1, "title" => "SQL"], ["id" => 4, "parent_id" => 2, "title" => "PDO"], ]; function groupByParent(array $rows): array { $childrenByParent = []; foreach ($rows as $row) { $parentKey = $row["parent_id"] ?? 0; $childrenByParent[$parentKey][] = $row; } return $childrenByParent; } ``` That grouping step keeps database work out of the recursive function. Fetch the rows once, index them by parent, then let the recursion walk the in-memory shape. ```php 20) { throw new RuntimeException("Category nesting is too deep."); } $branch = []; $parentKey = $parentId ?? 0; foreach ($childrenByParent[$parentKey] ?? [] as $row) { $branch[] = [ "id" => $row["id"], "title" => $row["title"], "children" => buildTree($childrenByParent, $row["id"], $depth + 1), ]; } return $branch; } $tree = buildTree(groupByParent($rows)); ``` The sample produces one Programming root, with PHP and SQL as children and PDO nested under PHP: ```text Programming ├── PHP │ └── PDO └── SQL ``` This version keeps the query layer separate from the traversal layer, and the traversal has a visible depth rule. If the business says a category can only nest five levels deep, set the limit to five and let the exception report when the data has drifted. Keep the SQL shape honest while you are there. A `parent_id` column should usually reference the same table, and deletes need a policy before the first production import. If deleting a parent deletes every child, write that down. If deleting a parent leaves children orphaned, the traversal code has to know what an orphan means. Recursion exposes those data-model decisions quickly because every bad relationship eventually becomes a bad path through the tree. One old LAMP-era mistake is to query for children inside the recursive function. The first version looks tidy because each call asks the database for its immediate children. On a real tree it becomes the familiar N+1 pattern: one query for the root, then another for each branch and nested branch. Fetch all relevant rows once and group them in PHP for small and medium trees. For very large trees, make the database work explicit with pagination, materialized paths, closure tables, or a recursive query, then let PHP render the result it received. ## Render Nested Output Carefully Once you have a tree, the recursive render function is straightforward. The important part is that output still needs escaping because recursion does not make HTML safer. ```php "; foreach ($items as $item) { $title = htmlspecialchars($item["title"], ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); $html .= "
  • " . $title . renderTree($item["children"]) . "
  • "; } return $html . ""; } ``` The recursive call is only responsible for structure. It does not get a free pass on the boring parts that keep web apps safe: escaping output, validating input shape, and keeping database queries away from the inner loop. ## How Do You Limit Recursion Depth in PHP? Limit PHP recursion by passing a depth counter into each call and rejecting input that would exceed a chosen maximum. A deeply nested JSON payload, imported category tree, or comment thread can become a denial-of-service problem if the code walks it recursively with no limit. Add a maximum depth when the input comes from outside the application. ```php $maxDepth) { throw new RuntimeException("Menu nesting is too deep."); } $rows = []; foreach ($items as $item) { $rows[] = ["label" => $item["label"], "depth" => $depth]; $children = $item["children"] ?? []; if ($children !== []) { if ($depth >= $maxDepth) { throw new RuntimeException("Menu nesting is too deep."); } $rows = [ ...$rows, ...flattenMenuWithLimit($children, $depth + 1, $maxDepth), ]; } } return $rows; } ``` A leaf at `maxDepth` is valid because the function does not descend again. A node with children at that depth throws before visiting a level beyond the limit. Cycles are the other quiet failure mode. A clean tree has one parent path from the root to each node. Bad imported data can produce `A -> B -> C -> A`, and a recursive function will keep walking that loop unless you remember what it has already visited. ```php 20) { throw new RuntimeException("Tree nesting is too deep."); } $branch = []; $parentKey = $parentId ?? 0; foreach ($childrenByParent[$parentKey] ?? [] as $row) { if (isset($seen[$row["id"]])) { throw new RuntimeException("Cycle detected at node " . $row["id"]); } $branch[] = [ "id" => $row["id"], "title" => $row["title"], "children" => buildTreeSafely( $childrenByParent, $row["id"], $seen + [$row["id"] => true], $depth + 1 ), ]; } return $branch; } ``` The seen map rejects a repeated node on a path this function visits. Starting at the root does not discover disconnected cycles or orphaned rows. Validate all parent IDs and check that every expected row appears in the result before accepting imported data. ## Memoization for Repeated Subproblems Some recursive functions repeat the same work, and the classic Fibonacci example makes recursion look worse than it has to be. ```php 0, 1 => 1]): int { if ($number < 0) { throw new InvalidArgumentException("Fibonacci needs a non-negative number."); } if (!array_key_exists($number, $cache)) { $cache[$number] = fibonacci($number - 1, $cache) + fibonacci($number - 2, $cache); } return $cache[$number]; } ``` Memoization is useful when the same subproblem appears again and again, such as a permission tree, a dependency graph, or a calculated category count. It saves repeated work but leaves the call stack just as deep, so it does not justify recursion when the input depth is unknown. The cache needs an owner as much as the function does. A request-local array is fine for a calculated menu or a short dependency walk. A shared cache needs invalidation rules near the data that changes the tree. If an admin moves a category, clears a parent, or imports a new comment thread, a cached recursive result can become stale. Recursion makes that stale structure visible because one wrong parent can move an entire branch. The cleanest recursive functions are often pure enough to test with arrays. Pass in a small tree, assert the flattened output, then pass in a cycle and assert the exception. If the function also queries the database, reads the session, prints HTML, and updates a cache, the recursion is no longer the hard part. The hard part is that the function has too many jobs. ## SPL Iterators PHP's Standard PHP Library has recursive iterators for filesystem traversal. If your job is to walk directories, reach for those before writing your own directory recursion. ```php isFile()) { echo $file->getPathname() . PHP_EOL; } } ``` The standard library has already handled a common recursive shape, including the iterator protocol around it. Use the library where it fits, then save custom recursion for your application data. ## When Should PHP Use a Loop Instead of Recursion? Use a loop when the problem is linear and the depth is unbounded or large. ```php = 0; $i--) { $stack[] = [$items[$i], 0]; } while ($stack !== []) { [$item, $depth] = array_pop($stack); $rows[] = ["label" => $item["label"], "depth" => $depth]; $children = $item["children"] ?? []; for ($i = count($children) - 1; $i >= 0; $i--) { $stack[] = [$children[$i], $depth + 1]; } } return $rows; } ``` The recursive version reads closer to a small, trusted menu. If the depth comes from a customer import, public API payload, or legacy table with no constraint, the explicit stack is easier to cap, log, and reason about under load. ## Common Pitfalls & Debugging ### The Base Case Is Missing or Unreachable Symptom: the function keeps calling itself until the process fails. Cause: the base case is missing, or the recursive input moves away from it. Fix: put the base case before the recursive call and prove that every call moves toward it. ### Deep Recursion Exhausts Memory Symptom: a large import or nested payload consumes memory and terminates the request. Cause: every unfinished call leaves another unfinished call on the runtime stack. Fix: enforce an application depth limit and use an explicit stack or queue when input depth can be large. ### An Iterative Alternative Is Clearer Symptom: a recursive counting function is harder to trace than the work itself. Cause: the data is linear rather than nested. Fix: use a loop for counting and an explicit stack or queue when control over memory and depth matters more than matching the data shape. ## Frequently Asked Questions ### What is a base case in recursion? A base case is a condition that stops recursion without making another recursive call. The function may return a value or simply return. The recursive step must also move each new input toward that case, or the function will continue consuming stack space. ### Does PHP have a recursion depth limit? PHP does not define one portable language-level depth limit for ordinary recursive calls. Available memory, the runtime stack, extensions, and environment settings can stop deep recursion. Application code should set its own depth limit when data depth is not trusted. ### When is recursion better than a loop? Recursion is often clearer when the data contains smaller versions of itself, such as category trees, nested comments, or directories with subdirectories. A loop is usually clearer for linear counting and safer when the depth can be large or unbounded. ### How do you prevent cycles in a recursive tree? To prevent cycles during PHP tree traversal, track node IDs on the current path and reject a repeated ID. A maximum depth provides a second guard. Database constraints and import validation should prevent invalid parent relationships before traversal begins. ### Does each recursive PHP call have its own local variables? Each recursive PHP call has its own local scope. References, objects, static variables, and globals can still share data between calls, so a new scope does not mean every value is copied independently. ### Can RecursiveIteratorIterator walk something other than the filesystem? PHP RecursiveIteratorIterator can traverse nested data through a RecursiveIterator. For example, RecursiveArrayIterator lets it walk nested arrays instead of directories. ### Does PHP optimize recursive function calls into a loop automatically? No. PHP has no tail-call optimization, so a recursive function still consumes one stack frame per call regardless of how the recursive call is written. Depth limits and iterative alternatives exist because PHP does not remove that cost automatically. ### Does calling array_map with a callback count as recursion? PHP array_map() is not recursive by itself; it applies a callback to array elements. The callback can use recursion if it calls itself or another function that eventually calls it again. ## Self-Check 1. Where should the base case appear: before or after the recursive call? 2. What does `countDown(3)` output? 3. What label and depth sequence does `flattenMenu($menu)` produce for the sample menu? 4. Which pair protects a tree traversal: a seen set and maximum depth, or sorting and pagination? 5. When should an explicit stack replace recursive calls: for small trusted trees, or for large or untrusted depth? ### Answers 1. Before the recursive call. A matching input returns before another call is added to the stack. 2. `3`, `2`, then `1`. Each call prints before subtracting one and descending. 3. Programming 0, PHP 1, SQL 1, AI Coding Tools 0. Children appear immediately after their parent. 4. Use a seen set and maximum depth. One detects repeated nodes, while the other caps the work. 5. Replace recursion when depth is large or untrusted. An explicit stack makes memory use and limits easier to control. ## Keep PHP Recursion Bounded Recursion matches data that contains smaller versions of itself. Limit the number of recursive calls, make the base case visible, and switch to iteration when the depth stops being predictable. To expose nested application data to another client, continue with [building an API in PHP](/programming/php/apis/custom-api/). --- ## Composite Images with PHP and GD URL: /programming/php/images/gd-overlapping-images/ Cluster: Programming > Php Use [PHP's GD extension](/programming/php/) when one image placed over another has to be saved as a merged file for storage, email, caching, or export. Think of the background as a printed card and the transparent overlay as a clear sheet placed on top. CSS keeps those two layers separate in the browser, while GD presses them into one permanent sheet of pixels. Choose CSS or GD based on whether you need to save the merged image. If the user is only seeing a badge, label, watermark preview, avatar stack, or product overlay in the browser, the [HTML tutorials hub](/programming/html/) covers the browser-side tools. HTML and CSS can stack separate assets cleanly, and the design remains editable. Use PHP/GD when the merged pixels need to exist after the request: a watermarked download, an exported certificate, a generated social image, a printable badge, or a product mockup. These examples require the PHP GD extension with JPEG and PNG support. Check `extension_loaded("gd")` and `gd_info()` in the same PHP environment that runs the image job. The `GdImage` type hints require PHP 8.0 or newer, while the promoted `readonly` properties require PHP 8.1 or newer. Keep the original files so you can regenerate the composite when the watermark or output size changes. Read this after [creating image thumbnails with PHP](/programming/php/images/thumbnails/). The thumbnail lesson covers resizing and generated derivative files; this lesson adds compositing, opacity, alpha handling, and output storage. Use the [HTML tutorials hub](/programming/html/) when the browser preview and saved image need the same composition rules. ## How Should PHP Choose Image File Paths? PHP should choose image paths from trusted application records rather than accept a path from the request. Do not pass raw form fields, query strings, or uploaded filenames into `imagecreatefromjpeg()` or `imagecreatefrompng()`. Use application records and generated filenames like this: ```php backgroundPath, $job->overlayPath, $job->targetPath, 880, 48, ); ``` Do not regenerate the same composite on every gallery request unless you have a measured reason. Generated files can be cached, backed up, reviewed, and invalidated when their inputs change. A dynamic image endpoint has to solve all of that per request, plus authorization and error handling. If the output is private, store it outside the public directory and serve it through an authorized download route. If the output is public marketing media, give it a stable public path and let the CDN do the boring work. ## How Do You Match a CSS Preview to a PHP Export? The browser preview should use the same source image, overlay asset, anchor point, and sizing rule as the PHP export. Otherwise the user approves one image and downloads another, which is a fast way to lose trust in a feature. A small composition model is enough here: ```php "bottom-right", "padding" => 48, "overlayWidth" => 220, "opacity" => 1.0, ]; ``` Use that model to render the preview and generate the file. The preview can still be HTML and CSS; the saved file can still be GD. The shared model is the contract between them. ## Common Pitfalls & Debugging ### Transparent Pixels Turn Black The resized PNG gets a black rectangle around the logo. The new true-color image was copied before alpha saving and a transparent fill were configured. Disable alpha blending on the destination, call `imagesavealpha()`, fill it with a fully transparent color, then resample the source. ### The Overlay Sits Outside the Canvas The overlay is clipped or missing from the saved file. The destination coordinates were calculated from preview pixels or without subtracting the resized overlay dimensions. Calculate coordinates from the final background and overlay sizes, then reject negative or out-of-bounds positions. ### Writing the Output File Fails The in-memory composite looks valid and the target file is missing. The target directory is absent or unwritable by the PHP process, or the encoder returned `false`. Create the directory during deployment, verify its ownership, and treat a failed `imagejpeg()` call as an exception. ## Frequently Asked Questions ### Can GD composite more than two images at once? GD combines more than two images by copying each source onto the same destination in separate calls. The copy functions take a single destination and a single source, so three or more layers means calling them repeatedly onto the same destination and saving once at the end. ### Does imagecopy resize the overlay? No. imagecopy copies the source rectangle at its current pixel dimensions. Resize the overlay with imagecopyresampled before compositing when its stored dimensions do not match the export. Keeping resize and copy as separate steps makes the final scale and position easier to test. ### Does GD support SVG overlays? No. GD is raster-only and has no SVG renderer, so a vector logo has to be rasterised to PNG at the size you need before GD can place it. Do that conversion outside GD. ### Can a GD overlay be reused across many backgrounds? A GD overlay can be reused across multiple backgrounds within one PHP process. Load it once with imagecreatefrompng and pass that same GdImage object into the composite call for every background in the batch rather than reopening the file each time. ### Does the CSS position used to preview an overlay need to match the PHP compositing coordinates exactly? A CSS preview and PHP export can use different pixel coordinates if they preserve the same overlay position and scale relative to the background. A CSS preview built with percentages or a scaled container can differ in pixels from the PHP coordinates as long as both express the overlay's position relative to the same corner and at the same relative scale. ## Save the Image Your Page Needs Generate the composite when its inputs change, then reuse the saved file. Keep private outputs outside the public directory and check that the encoder produced a readable, non-empty image before exposing the result. Start with [image thumbnails in PHP](/programming/php/images/thumbnails/) when you need derivative files, then use this lesson when the derivative is made from more than one source image. The [HTML tutorials hub](/programming/html/) covers the browser-side preview. --- ## Create Image Thumbnails with PHP URL: /programming/php/images/thumbnails/ Cluster: Programming > Php A thumbnail needs its own smaller file instead of a CSS box around the full-size asset. A current [PHP app](/programming/php/) should create thumbnail assets at upload time, import time, or in a background job, then serve those generated files through ordinary HTML. Think of the original as a master negative and each thumbnail as a print made for one frame. Keep the master, name each print by its intended size, and regenerate those prints when the frame changes. Use PHP for the file-generation step, then let the browser handle layout, `srcset`, lazy loading, and display rules. Mixing those jobs is how a small image feature becomes a slow gallery and, if the path handling is loose, a file-disclosure bug with nice square corners. These examples require the PHP GD extension with JPEG and PNG support. Check `extension_loaded("gd")` and `gd_info()` in the same PHP environment that runs the image job. Explicit `GdImage` type hints require PHP 8.0 or newer, while the promoted `readonly` properties require PHP 8.1 or newer. Keep the original image and generate each thumbnail size from it, so later changes do not compound earlier compression. Read this after [working with text files in PHP](/programming/php/files/text-files/) if your app now needs to create real image files. The [HTML tutorials hub](/programming/html/) covers the stable markup and responsive display rules that generated files still need. ## Which Thumbnail Sizes Should You Generate? Generate the sizes your interface uses. A gallery might keep one original and two thumbnail files: ```text storage/ originals/ product-1329.jpg thumbnails/ product-1329-320.jpg product-1329-640.jpg ``` The original file stays available for a detail view, download, or later regeneration. The thumbnail files are cheap to serve in the grid. If the design changes from 240-pixel cards to 320-pixel cards, regenerate the derivative files from the original. Do not keep resampling thumbnails from thumbnails until the image has been through the washing machine a few times. For a small PHP app, GD is still a reasonable teaching tool. If the site is image-heavy, needs WebP/AVIF pipelines, or does a lot of editorial cropping, you eventually want a dedicated image library, a queue worker, or an image service. But the geometry is the same either way: load a source, calculate dimensions, resample into a destination, save the derivative. ## How Should PHP Choose the Source Image? PHP should look up the image by an application ID and use its stored server-side path. Accepting an image path from the query string and stripping `..` out of it is still a common move, and it is exactly the sort of thing that should make you uneasy. Removing one substring leaves the request in control of the path. The safer move is to use an application identifier: ```php originalPath, __DIR__ . "/../public/uploads/thumbs/{$photo->thumbnailBaseName}-320.jpg", 320, 320, ); createJpegThumbnail( $photo->originalPath, __DIR__ . "/../public/uploads/thumbs/{$photo->thumbnailBaseName}-640.jpg", 640, 640, ); ``` If the image came from an upload form, validate the upload before this step. Check the upload error code, cap the file size, store the original under a generated name, keep private originals out of the public directory when needed, and run image inspection on the server. The PHP manual's upload examples are deliberately low-level; your application still owns the storage policy. The master-negative rule keeps this regeneration predictable. Every 320-pixel or 640-pixel derivative comes from the accepted original, never from another thumbnail that has already lost detail. ## How Do You Display PHP Thumbnails in HTML? Once PHP has generated the files, the page should point at the thumbnail files directly: ```html Walnut desk organizer with two notebooks ``` The example assumes square source files at least 640 pixels wide. For proportional thumbnails, set each `srcset` width descriptor and the image width and height to the actual generated dimensions. PHP made the derivative files, HTML tells the browser which assets exist, and CSS controls cropping, borders and layout. The HTML `loading` attribute requests lazy loading. Do not route every thumbnail request through `thumbnail.php` unless you have a measured reason. A generated file can be cached by the browser, CDN, or static server. A PHP thumbnail endpoint has to solve caching, validation, path safety, and error handling on every request, which is worth it in a few cases but usually overbuilt for a basic gallery. ## Common Pitfalls & Debugging ### Large Uploads Exhaust Memory A large photo passes the file-size check but thumbnail generation terminates. Decoded pixel data needs far more memory than the compressed upload. Inspect dimensions before decoding, reject images above the application's pixel limit, and move heavier jobs away from the request path. ### PNG Transparency Becomes Black A transparent PNG gets a black background after resizing. The destination image was created without alpha preservation and a transparent fill. Configure alpha on the destination before resampling, or keep the JPEG-only boundary shown in this tutorial until PNG output is implemented deliberately. ### The Target File Never Appears No thumbnail is written even though the resize calculations finish. The destination directory is absent, the PHP process cannot write there, or `imagejpeg()` returned `false`. Create and own the directory during deployment, then throw when the encoder reports failure. ## Frequently Asked Questions ### Is WebP always smaller than JPEG? WebP is not always smaller than JPEG at the quality you need. A well-optimised JPEG can occasionally beat a poorly configured WebP export, so measure the actual output rather than assuming the newer format wins by default. ### Can a thumbnail use a different format from its source? A PHP thumbnail can use a different format from its source when the GD build supports both formats, though a transparent PNG converted to JPEG must be flattened onto a solid background first, because JPEG carries no alpha channel. ### Does CSS resizing reduce the image download? CSS resizing does not reduce the download because the browser still retrieves the selected image resource. Generate smaller files and describe them with srcset and sizes so the browser can choose an asset close to the space available. ### Does GD rotate photos to match their EXIF orientation? GD does not automatically apply EXIF orientation, so a phone photo that looks upright in a viewer can come out sideways in the thumbnail. Read the EXIF orientation and apply the required rotation or mirror operation before resizing. ### Does cropping to a fixed size distort an image whose aspect ratio does not match? Cropping a thumbnail preserves its proportions when the crop rectangle matches the target aspect ratio. Forcing a different ratio into a fixed width and height stretches the image. Calculate a crop rectangle that matches the target ratio first, centering it on the source image, then resize that rectangle to the final dimensions so proportions stay correct. ## Save the Image Your Page Needs Generate each thumbnail from the original, store the actual output dimensions, and serve the saved file. Verify that the encoder produced a non-empty image before adding it to the gallery. Build the file pipeline first, then make the thumbnail grid pleasant. For saved overlays or watermarks, read [composite images with PHP and GD](/programming/php/images/gd-overlapping-images/). The [HTML tutorials hub](/programming/html/) covers the browser side of gallery markup. --- ## The LAMP Stack Explained URL: /programming/php/lamp-stack/ Cluster: Programming > Php LAMP combines Linux, Apache, MySQL and [PHP](/programming/php/) to run database-backed websites. Linux runs the server, Apache handles web requests, MySQL stores data and PHP builds the response. Debugging LAMP is easier when you know which component failed. A browser does not query MySQL directly, and MySQL does not send HTML. Each request moves through the stack in order, then the response travels back to the browser. ## What Does Each Letter in LAMP Mean? LAMP stands for Linux, Apache, MySQL and PHP. Each component has a different job in serving a web page. | LAMP Stack Letter | Component Responsibility in the Request | | Linux | Runs the operating system, processes, files, networking, users, and permissions underneath the application. | | Apache | Listens for HTTP requests, selects a virtual host and file or handler, and returns the resulting HTTP response. | | MySQL | Stores structured application data and enforces database permissions, indexes, constraints, and transactions. | | PHP | Runs application code, validates the request, queries MySQL, and renders the HTML or other response body. | Linux supports the other processes, but it does not decide which PHP file handles a URL. Apache owns the HTTP request boundary and routing configuration. PHP owns the application's behavior and generated response. MySQL owns stored data and the rules attached to it. The parts can change without changing the application's basic job. MariaDB can fill the database role, and PHP can run through Apache's PHP module or through PHP-FPM. The name LAMP describes the classic combination, while the boundaries explain how current installations still work. ## How Does a LAMP Stack Handle a Web Request? In a LAMP stack, Apache receives the request, PHP runs the application code and queries MySQL when needed, then Apache returns PHP’s response to the browser. A request for `https://example.test/posts/1`, for example, follows this path through the four components: 1. Linux accepts the network traffic and runs the Apache process. 2. Apache matches the hostname and path to a virtual host and PHP entry point. 3. PHP reads the route, validates input, and sends a query through a database driver such as PDO. 4. MySQL checks the account's permissions, runs the query, and returns rows. 5. PHP escapes the data and builds an HTML response. 6. Apache sends the HTTP status, headers, and body back to the browser. The browser now has the generated response. When a page fails, identify the last boundary that worked instead of treating LAMP as one large program. A connection error points toward PHP, the MySQL listener, credentials, or database permissions. A raw PHP source file in the browser points toward Apache's PHP handler. A 403 response may come from Apache access rules or from the PHP application itself. Check the logs to find which component rejected the request. HTTP status codes help locate the failing boundary. A 404 means Apache or the application could not map the requested path. A 500 means a server-side component failed after accepting the request. A successful 200 response with missing data points farther into PHP rendering or the SQL result. ## When Should You Use a LAMP Stack? LAMP suits PHP websites, content management systems and applications that store relational data. It is especially practical when your hosting already supports Apache and MySQL. Use LEMP if you prefer Nginx instead of Apache; Windows and macOS variants are often called WAMP and MAMP. Managed hosting can handle server maintenance if you would rather focus on the application. ## Build a Minimal PHP and MySQL Page This example creates one MySQL table, queries it from PHP with PDO, and lets Apache serve the resulting `index.php`. It assumes the operating system packages for Apache, PHP, the PDO MySQL driver, and MySQL are already installed. ### Create the MySQL Data Create a small database and a row that the page can display: ```sql CREATE DATABASE lamp_demo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE lamp_demo; CREATE TABLE posts ( id BIGINT UNSIGNED PRIMARY KEY, title VARCHAR(200) NOT NULL ); INSERT INTO posts (id, title) VALUES (1, 'Your first LAMP page'); ``` The web application needs an account with only the permissions it uses. MySQL checks privileges for each statement, so a read-only page does not need an administrative account. ```sql CREATE USER 'lamp_reader'@'127.0.0.1' IDENTIFIED BY 'replace-with-a-generated-secret'; GRANT SELECT ON lamp_demo.* TO 'lamp_reader'@'127.0.0.1'; ``` Supply the real password through the server's secret or environment configuration. Do not place it inside the public PHP file or commit it with the project. ### Query MySQL from PHP Place the example file at `/srv/lamp-demo/public/index.php` inside the public document root. PDO opens the connection, runs a fixed query, fetches one associative row, and escapes the title before it enters HTML. ```en PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ], ); $statement = $pdo->query( "SELECT id, title FROM posts WHERE id = 1" ); $post = $statement->fetch(); if ($post === false) { http_response_code(404); exit("Post not found"); } ?> <?= htmlspecialchars( $post["title"], ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8", ) ?>

    ``` A request for the page should return an H1 containing `Your first LAMP page`. The browser receives only the rendered HTML. It never receives the password, PDO object, or SQL statement. ### Serve the Page with Apache An Apache virtual host connects the hostname to the public directory. The exact configuration directory depends on the operating system package. This Debian or Ubuntu example uses the distribution's explicit Apache log path: ```apache ServerName example.test DocumentRoot "/srv/lamp-demo/public" DirectoryIndex index.php AllowOverride None Require all granted ErrorLog "/var/log/apache2/lamp-demo-error.log" CustomLog "/var/log/apache2/lamp-demo-access.log" combined ``` This configuration tells Apache where the public files live and permits requests to that directory. It does not install PHP or choose its execution model. Apache still needs either a working PHP module or a FastCGI mapping to PHP-FPM. For PHP-FPM, add the password to the active pool configuration so `getenv("DB_PASSWORD")` can read it. Replace the placeholder with the deployment secret: ```ini env[DB_PASSWORD] = replace-with-a-generated-secret ``` When Apache runs PHP through `mod_php`, the equivalent mechanism is `SetEnv DB_PASSWORD "replace-with-a-generated-secret"` in the virtual host. Keep the real value out of the public document root and version control in either model. After reloading Apache, request the page and inspect the response: ```text curl -i -H "Host: example.test" http://127.0.0.1/ HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8 ...

    Your first LAMP page

    ``` A successful response proves the whole path: Apache found the virtual host, PHP executed, PDO connected, MySQL returned a row, and Apache sent the rendered result. ## Choose the Apache Module or PHP-FPM `mod_php` loads the PHP interpreter into Apache processes. Apache recognizes a PHP file, hands it to the embedded PHP handler, and receives the generated output inside the same process model. PHP-FPM runs PHP workers as a separate service. Apache's `mod_proxy_fcgi` can forward PHP requests to an FPM socket or TCP listener, then return the generated response. The PHP manual describes FPM pools that can use different users, groups, environments, limits, and `php.ini` settings. ```apache SetHandler "proxy:unix:/run/php/php-fpm.sock|fcgi://localhost/" ``` The socket path varies by operating system and PHP package, so copy it from the active FPM pool's `listen` setting. Apache also needs `mod_proxy` and `mod_proxy_fcgi` enabled. Choose one execution path and document it. Loading `mod_php` while also forwarding `.php` files to FPM creates confusing configuration and can make the command-line PHP version, Apache module version, and FPM version disagree. ## Modern LAMP: Containers, Managed Hosting, and LEMP Containers and managed hosting are the operational successors to assembling every LAMP component on one manually administered server. They keep the same responsibilities and package them differently or move part of the maintenance to a provider. A container deployment often runs the web server, PHP-FPM, and MySQL as separate services. The database needs persistent storage, the application needs secrets, and the services need health checks and a private network. Replacing one server with several containers does not make those boundaries disappear. Managed PHP hosting can handle Linux updates, web-server configuration, TLS, backups, and database operations. Check exactly which jobs the provider owns. Application updates, database queries, credentials, and error handling usually remain the application's responsibility. Replacing Apache with nginx creates the common LEMP variant. Nginx accepts the HTTP request and forwards PHP files to PHP-FPM, while Linux, MySQL, and PHP keep their original roles. Apache with PHP-FPM is also a current design, so adopting FPM does not require changing web servers. Choose the deployment model from operational needs. A managed host reduces routine server administration, while containers make service boundaries and deployment artifacts explicit. A small site can still run well on one maintained server when its patching, backups, monitoring, and recovery responsibilities have clear owners. ## Common Pitfalls & Debugging ### PHP Works in the Terminal but Fails in Apache The command-line interface, `mod_php`, and PHP-FPM are different server APIs and can load different configuration files. A successful `php index.php` test proves the CLI works, but it does not prove Apache has a PHP handler. Create a temporary diagnostic page that prints `PHP_SAPI` and `php_ini_loaded_file()`, request it through Apache, record the result, and delete the page. If the browser downloads the file or displays PHP source, fix the Apache handler immediately and keep the site unavailable until source disclosure is closed. ```php ` block and its `Require` directive, then check filesystem ownership and modes. With PHP-FPM, the pool user also needs access to the script. Do not solve the problem with `chmod 777`, because world-writable application files create a separate security failure. Database permission errors come from a later boundary in the request. An `Access denied` message from MySQL means the PHP code reached the database server, but the account, host match, password, or grants did not authorize the connection or query. ### The Page Is Blank or Returns a 500 Error Start with the configured logs instead of guessing at default file paths. Package layouts differ, and containers may send logs to standard output or a platform collector. - Apache writes request failures to the destination set by its `ErrorLog` directive. - PHP writes errors to the configured `error_log` when `log_errors` is enabled. - PHP-FPM has a global `error_log`, and a pool can set its own PHP error log. - MySQL records server startup and runtime problems in its configured error log, with recent events also available through Performance Schema when that output is enabled. Keep `display_errors` enabled only in a private development environment. The PHP manual warns that displayed production errors can expose confidential details, including database credentials. Production should log the full error and return a generic response to the browser. ## Production Baseline - Keep Linux, Apache, PHP, and MySQL on supported, patched releases. - Expose only the public document root, with configuration and secrets stored outside it. - Give the MySQL application account only the privileges required by the application. - Use PDO prepared statements for request values and escape database text when rendering HTML. - Disable displayed PHP errors in production and verify that Apache, PHP, FPM, and MySQL logs reach an operator. - Back up the database and test a restore before treating the backup job as complete. - Terminate HTTPS at Apache, a trusted proxy, or the managed platform, and document which layer owns it. The stack is dependable when each component has a clear job and an observable failure path. A plain request, one PHP entry point, one least-privilege database account, and known log destinations make a better starting point than a large local bundle nobody can explain. ## Frequently Asked Questions ### Is the LAMP stack outdated? The LAMP stack is not inherently outdated. Its supported components receive releases and security updates, and the request model still suits content sites and database-backed web applications. The deployment style has changed, with PHP-FPM, containers, and managed services replacing many hand-built server installations. ### Does a LAMP server need a separate application server? A typical LAMP application does not need another application server. Apache and PHP-FPM can handle its HTTP requests, though background jobs or real-time features may need separate workers. ### Can one server run several PHP versions at once? One server can run multiple PHP versions using a separate PHP-FPM installation or service for each version. Configure each service with its own socket, then route each site to the appropriate socket. Pools within one FPM service use that service’s PHP version. ### Does moving from Apache to nginx change the PHP code? Moving from Apache to Nginx usually leaves the PHP application code unchanged when both use PHP-FPM. Translate Apache-specific routing, rewrite and access rules into Nginx configuration, and check any application assumptions about server variables. ### Does PHP-FPM run as the same operating-system user as Apache or nginx? PHP-FPM can run as a different operating-system user from Apache or Nginx. A dedicated pool user helps limit access to other applications’ files, provided filesystem permissions are configured accordingly. ### Can a GUI tool like phpMyAdmin still connect to a MySQL database running in Docker? phpMyAdmin can connect to MySQL in Docker over a shared private container network. A GUI on the host needs a reachable MySQL port, which can be bound to localhost. Neither arrangement requires exposing the database to the public internet. ## Self-Check 1. Which LAMP component accepts the HTTP request and selects the virtual host: Linux, Apache, MySQL, or PHP? 2. What happens when the PHP example runs without `DB_PASSWORD` in the FPM pool or Apache environment? 3. Which MySQL account should the read-only page use: `root`, `lamp_reader`, or an anonymous account? 4. What does the successful curl response prove about the request path through the stack? 5. If PHP works in the terminal but Apache returns raw PHP source, which boundary should be checked first? ### Answers 1. Apache. It owns the HTTP boundary, chooses the matching virtual host, and passes PHP files to the configured handler. 2. PHP throws a RuntimeException. The guard stops the page before PDO attempts a connection with a missing secret. 3. `lamp_reader`. Its SELECT-only grant matches the page's database work and avoids administrative privileges. 4. Every boundary completed. Apache found the host, PHP executed, PDO connected, MySQL returned the row, and Apache sent the rendered HTML. 5. Apache's PHP handler. The CLI and web server use different server APIs, so a working CLI does not prove that mod_php or PHP-FPM is configured. ## Next Steps Start with the [PHP tutorials hub](/programming/php/), then build the database boundary with [PHP and MySQL with PDO](/programming/php/mysql/pdo/). The [SQL tutorials hub](/programming/sql/) covers queries and schema design, while [PHP security fundamentals](/programming/php/security/fundamentals/) covers the input, output, session, and deployment checks around the example. --- ## Streaming Media with PHP URL: /programming/php/media/streaming-media/ Cluster: Programming > Php [PHP](/programming/php/) can enforce access control, keep media files outside the public web root, and make casual hotlinking harder. It cannot turn a playable full-length MP3 into something no user can capture. If a browser can play the whole file, the client has received the whole file or can record the output. Think of PHP as the ticket desk at a venue. It checks whether the visitor may enter and chooses the right performance, while the sound system carries the audio. PHP should decide who may hear the file; the web server, framework response layer, CDN, or object storage layer should usually carry the bytes. This is an advanced PHP tutorial because it crosses application code, HTTP headers, filesystem paths, media playback, and server delivery behavior. Read it after [working with files in PHP](/programming/php/files/text-files/) and [using sockets in PHP](/programming/php/networking/sockets/) if you want the lower-level protocol boundaries fresh in your head. ## What Should PHP Handle When Streaming Media? PHP should check who may access each media file and select the authorised file before delivery starts: - Check the logged-in user, subscription, purchase, or preview entitlement. - Map a stable media ID to a server-owned filename. - Keep media files outside the public document root. - Choose whether the user gets a preview file or the full file. - Log the playback or download event if the product needs an audit trail. That is application logic, and it belongs in PHP because it depends on sessions, accounts, orders, and database state. The long transfer often belongs to a different layer. `readfile()` streams without loading the whole file into PHP memory, but worker concurrency, execution limits, output buffering, range support, and server configuration decide whether direct delivery is viable. Measure those constraints before choosing PHP delivery. ## How Do You Protect Private Media Paths? Protect private media by looking up a server-controlled path from an authorised media ID. Do not accept a filename from the query string: ```php file($path); $size = filesize($path); $publicName = str_replace(["\"", "\r", "\n"], "", basename($track["public_name"])); if ($mime === false || $size === false) { http_response_code(500); exit; } header("Content-Type: " . $mime); header("Content-Length: " . $size); header('Content-Disposition: inline; filename="' . $publicName . '"'); header("Cache-Control: private, no-store"); readfile($path); exit; ``` `readfile()` reads a file and writes it to the output buffer. The PHP manual also notes that `readfile()` itself should not create a memory problem for large files, although output buffering can still change the result in a real deployment. The more valuable the media and the larger the file, the more you should want PHP to authorize the response and then hand delivery to something built for delivery. ## Why Do Media Players Need Range Requests? Audio players often need byte ranges so a browser can seek, pause, resume, or request part of a file. MDN documents the `Range` header as the request header that asks a server to return only part of a resource. When the server accepts the request, it answers with `206 Partial Content`. That is why media serving is more subtle than a normal download. A route that always returns the whole file with `200 OK` may appear to work until a user seeks into the middle of the track or a mobile browser resumes playback. You can implement range handling in PHP, but it is easy to get wrong: invalid ranges, off-by-one `Content-Range` values, output buffering, and cache headers all have to agree. Let the web server or a framework component handle that work unless the application has a specific reason to own the protocol code. Symfony's `BinaryFileResponse`, for example, handles `Range` and `If-Range` headers and supports `X-Sendfile` style handoff. That framework component shows how mature PHP tooling treats file delivery as response infrastructure. ## How Do You Let nginx Send a Private Media File? Let nginx send a private media file by configuring an internal location and returning an X-Accel-Redirect header from PHP after checking access. The browser requests `/media/track/abc123`, PHP authenticates the request and maps `abc123` to a private file, and PHP returns a response that tells the web server or storage layer what to serve. The web server then handles the bytes, range requests, and transfer behavior. On nginx, that often means an internal location plus an `X-Accel-Redirect` header: ```php
    Play audio ``` The `preload="metadata"` hint asks the browser to learn enough about the file without pulling the whole track immediately. The server still has to enforce access on every request. In this context, browser attributes are presentation hints rather than authorization. For previews, consider using a separate preview file rather than trying to hide the full file behind JavaScript. A thirty-second sample and a paid full download are easier to reason about than a full-length file that the application pretends is only streamable. ## Common Pitfalls & Debugging ### Seeking Restarts or Fails Dragging the audio position restarts playback or does nothing. The route always returns the complete file with `200 OK` and does not honor byte ranges. Hand delivery to a range-aware web server, storage service, or tested framework response instead of improvising partial-content headers. ### Headers Have Already Been Sent Symptom: PHP warns that headers cannot be modified, or the media response contains stray text. Output was written before the status and media headers. Keep the delivery route free of template output and debugging text, set every header first, then transfer the file and exit. ### Authorization Is Bypassed by a Public File A signed-out visitor can open the storage URL directly. The file still lives under a public path even though an authorized PHP route also exists. Move the source outside the public document root or mark the server location internal so every request crosses the entitlement check. ## Can PHP Prevent Users from Copying Media? PHP access checks cannot stop an authorised listener from recording or saving playable media. They can stop unauthorised requests when every file delivery goes through the check. A complete private-media setup should also address: - Public URLs that expose the media directory. - Hotlinking to a static file path that bypasses PHP. - Serving paid files to users who no longer have access. - Query-string path traversal through a file parameter. - PHP workers being tied up for every large transfer. Keep access checks and private storage in place, but do not promise that playable media cannot be copied. ## Frequently Asked Questions ### Does an audio or video element need crossorigin for same-origin media? Same-origin audio and video do not need a crossorigin attribute. Cross-origin media may need CORS settings when a script reads its data, such as drawing video frames to a canvas. ### Can a protected video player still use a poster image? A video player using an authorised media route can still display a poster image. The poster loads from a separate URL, which can be an authorised image route or a public placeholder. The audio element has no poster attribute. ### Does the browser download the whole file before playing? A browser can start playing supported media before downloading the whole file. Startup and seeking depend on the file format, metadata location, connection and server response; byte-range support lets the player request specific portions. ### Should a streaming route log every playback request? A PHP media route should log only the playback events needed for its audit or operational purpose. Multiple range requests can belong to one playback, so do not count every request as a separate listen. Avoid recording tokens or unnecessary personal data. ### Do HTTP range requests behave the same across browsers? HTTP range responses follow a shared standard, but browsers can request different byte ranges and buffer media differently. Test seeking and resuming in the browsers you support. A server may return the full file with 200 OK when it ignores a range, but players that rely on partial content may then seek poorly. ## Self-Check 1. Multiple choice: Which layer should normally transfer a large authorized media file: PHP template rendering, a web server or storage layer, or browser JavaScript? 2. Multiple choice: Which response status indicates a successful byte-range response: `200`, `206`, or `404`? 3. Predict the output: What status code and response body does the snippet after this list produce when `$allowed` is `false`? 4. Multiple choice: Why does a duplicate public file bypass protection: it skips the entitlement check, ignores MIME detection, or disables range requests? 5. Multiple choice: What does `preload="metadata"` control: the browser's pre-playback fetch hint, server authorization, or copy prevention? ```php Php A dynamic PHP website reads data from MySQL and uses it to generate HTML. This project in the [PHP web-app path](/programming/php/) builds a notes app with a list, forms, and queries that restrict each note to its owner. The connection defaults below are for a local demo. In production, require explicit database settings and stop with a generic error if they are missing. You need PHP with pdo_mysql and mbstring, a running MySQL database, and basic HTML and SQL knowledge. These snippets show the main parts of the app rather than a complete downloadable project. Keep request handling, database queries, and HTML templates in separate files so you can change one without searching through the others. ## Guide Path This project path adds one layer at a time: 1. Dynamic site with PHP and MySQL.2. [News and comments CRUD](/programming/php/mysql/news-comments/).3. [Search application](/programming/php/mysql/search-application/).4. [Custom API capstone](/programming/php/apis/custom-api/). Pause for authentication and session hardening before exposing account-specific API behavior. That material has its own reference in [PHP authentication and sessions](/programming/php/security/auth-sessions/). ## How Should a Small PHP and MySQL App Be Organized? A small PHP and MySQL notes app can separate the public entry point, query functions, and HTML views into three folders: ```text public/ index.php css/ src/ db.php notes.php csrf.php views/ layout.php notes-index.php notes-new.php notes-edit.php ``` That is enough for a small app. `public/index.php` receives the request, `src/db.php` owns the PDO connection, `src/notes.php` owns note queries, and `views/` renders HTML. Once the app grows, you can move to Laravel, Symfony, Slim, CakePHP, or Fat-Free Framework. The early boundary still pays off because you already separated request handling, database access, and rendering. ## How Does PHP Connect to the MySQL Database? Put the database connection in one file and read credentials from the environment. A tutorial can show placeholders, but a real app should not commit database passwords. ```php PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ], ); } ``` Exception mode makes failed queries visible where the app can log and handle them. The [PDO and MySQL guide](/programming/php/mysql/pdo/) explains the setting in depth; this project applies it at the shared connection boundary. ## Create the Users and Notes Tables ```sql CREATE TABLE users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE notes ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, title VARCHAR(255) NOT NULL, body TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NULL, INDEX notes_user_created_idx (user_id, created_at), CONSTRAINT notes_user_fk FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ); ``` If your database supports foreign keys for the engine you use, add them. The database should know that a note belongs to a user. That is the shape of the data, while authorization remains an application responsibility. For MySQL, make sure the storage engine and deployment settings enforce the constraints you expect. MyISAM does not support foreign keys, so a schema that depends on referential integrity should use InnoDB and keep foreign-key checks enabled. If this is your first PHP and MySQL project, use InnoDB and prove the constraint exists before writing application code around it. A quick `SHOW CREATE TABLE notes;` is often enough to catch a migration that looked fine in the editor but landed differently on the server. ## How Does PHP Create and Read Notes? PHP creates and reads notes through prepared INSERT and SELECT statements. Put those queries in functions that accept the current user’s ID. ```php prepare( "SELECT id, title, created_at FROM notes WHERE user_id = :user_id ORDER BY created_at DESC LIMIT 20", ); $statement->execute(["user_id" => $userId]); return $statement->fetchAll(); } function createNote(PDO $pdo, int $userId, string $title, string $body): void { $statement = $pdo->prepare( "INSERT INTO notes (user_id, title, body) VALUES (:user_id, :title, :body)", ); $statement->execute([ "user_id" => $userId, "title" => $title, "body" => $body, ]); } ``` These functions can be checked independently of the HTML templates. Add update and delete functions while the table shape is still small: ```php prepare( "UPDATE notes SET title = :title, body = :body, updated_at = CURRENT_TIMESTAMP WHERE id = :id AND user_id = :user_id", ); $statement->execute([ "id" => $noteId, "user_id" => $userId, "title" => $title, "body" => $body, ]); return $statement->rowCount() === 1; } function deleteNote(PDO $pdo, int $userId, int $noteId): bool { $statement = $pdo->prepare( "DELETE FROM notes WHERE id = :id AND user_id = :user_id", ); $statement->execute([ "id" => $noteId, "user_id" => $userId, ]); return $statement->rowCount() === 1; } ``` Notice the `user_id` check on every write. A multi-user app must match the note and its owner together. The tutorial uses `requireUserId()`, defined in the linked [authentication and sessions reference](/programming/php/security/auth-sessions/), and each write checks ownership in its WHERE clause. An unchanged UPDATE can return zero; the edit handler must distinguish that from a missing note if it needs to report success accurately. ## How Does PHP Route Requests to the Notes App? The PHP front controller checks the request path and method, then calls the matching note function. Load the authentication helper from the linked session guide before using requireUserId(). ```php $title, "body" => $body]; require __DIR__ . "/../views/notes-new.php"; return; } createNote( db(), $userId, $title, $body, ); header("Location: /notes", true, 303); return; } if ($path === "/notes/delete" && $method === "POST") { verifyCsrfToken($_POST["csrf_token"] ?? ""); $deleted = deleteNote( db(), $userId, (int) ($_POST["id"] ?? 0), ); if (! $deleted) { http_response_code(404); exit("Note not found"); } header("Location: /notes", true, 303); return; } http_response_code(404); require __DIR__ . "/../views/404.php"; ``` This plain controller exposes the request shape that frameworks organize for larger apps. Laravel and Symfony wrap the same concerns in routing, controllers, requests, validation, middleware, and responses. As an exercise, add `GET /notes/new`, `GET /notes/edit`, and `POST /notes/update` with the same dispatch pattern before introducing a router. The `303` redirect after a successful write is intentional. It keeps a browser refresh from resubmitting the same form. Old PHP tutorials often skipped this, then left readers wondering why a duplicated record appeared after hitting reload. ## How Does the PHP Form Keep Entered Values? The PHP form keeps entered values by receiving an $old array from the handler and escaping each value when rendering it. Error messages should appear beside the affected field. ```php
    ">

    ``` The template is allowed to be plain, but it should not be careless. Keep the form action explicit, keep the method as `POST` for writes, and escape both the old input and the error message before rendering them. For edit screens, use the same template shape with a different action and the current row as the starting values: ```php
    "> ">
    ``` The hidden ID tells the handler which row the browser requested. Authorization comes from the `WHERE id = :id AND user_id = :user_id` clause, which limits the update to the authenticated user's note. ## What Should PHP Validate Before Saving a Note? PHP should reject an empty title or body and enforce lengths that fit the MySQL columns before saving a note. ```php 255) { $errors["title"] = "Use no more than 255 characters for the title."; } if ($body === "") { $errors["body"] = "Write something before saving."; } if (strlen($body) > 65535) { $errors["body"] = "Keep the note body within 65,535 bytes."; } return $errors; } ``` Database constraints protect the stored data, while validation helps the human fix the form before the write is attempted. The full [form validation in PHP](/programming/php/forms/spelling-validation/) guide separates those layers: browser hints, server-side checks, CSRF, database constraints, and output escaping. ## CSRF Protection A same-site form can still be submitted by a hostile page unless the server checks that the request came from a form it issued. A small PHP app can start with a session-backed CSRF token. ```php "> ``` Verify the submitted token before the write reaches the database: ```php verifyCsrfToken($_POST["csrf_token"] ?? ""); ``` CSRF protection closes the cross-site form risk. Authentication still identifies the current user, and authorization still decides which note that user may change. ## Escape Output Every Time Prepared statements protect values as they enter SQL. HTML output needs its own protection, so escape each user-supplied note title before rendering it. ```php

    ``` Plain PHP repeats this call because every data-to-HTML crossing needs protection. A framework template engine can escape by default, while a small hand-rolled app keeps the call beside the output. ## When Does the Notes App Need a Transaction? The PHP notes app needs a transaction when several related writes must all succeed together. The first version creates one note row at a time. Later, you may create a note, attach tags, and write an audit event in one request. At that point, use a transaction so the database does not keep half the change. ```php beginTransaction(); try { createNote($pdo, $userId, $title, $body); $noteId = (int) $pdo->lastInsertId(); foreach ($tags as $tag) { attachTag($pdo, $noteId, $tag); } $pdo->commit(); } catch (Throwable $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } throw $e; } } ``` Use a transaction when two related database changes must either both succeed or both be rolled back. ## Passwords and Sessions The schema above includes `password_hash` because most database-backed sites eventually need users. Use PHP's `password_hash()` and `password_verify()` rather than inventing a hashing scheme. The login/session details live in [PHP authentication and sessions](/programming/php/security/auth-sessions/), but the database app should not teach a pattern that boxes you into plain passwords later. For the project path, treat account handling as the point where this beginner app hands off to [PHP security fundamentals](/programming/php/security/fundamentals/) and the dedicated auth-session material. Start with the notes table and add real users only when you can protect the session, CSRF token, password reset, and authorization checks properly. ## Check the Notes App Before Adding Features This first project is finished when the app can list notes, create a note, edit a note, delete a note, reject invalid input, redirect after successful writes, escape output, and keep all SQL inside `src/notes.php`. That gives you a useful stopping point before the project starts collecting features. Do not add comments, tags, search, uploads, or account settings until the small shape is clean. Those features arrive in the next tutorials, and they are much easier to follow when the first app has one clear purpose. Use this quick review checklist before moving on: 1. One file explains the database connection.2. One file explains note queries and writes.3. Templates receive data instead of querying the database.4. Writes use `POST`, validation, CSRF, and a redirect.5. Every rendered user value passes through `htmlspecialchars()`.6. Each write filters by the current user, even in tutorial form. Check these behaviors before adding more features. ## Local Verification Do a manual pass before the next tutorial. Create two notes, edit one, delete the other, refresh after each write, and confirm no duplicate row appears. Then submit an empty title and confirm the form shows the old body instead of discarding it. Finally, type a title such as `` and confirm the browser renders the value as text instead of executing it. That final check catches the exact mistake old PHP snippets made for years. SQL injection and XSS are separate failures, and a prepared statement protects only the database side. Safe storage can still feed unsafe output. Also test the authorization shape, even with a fake user helper. Change a note ID in the edit or delete form and make sure the write function still filters by `user_id`. For DELETE, zero affected rows means no owned note matched. For UPDATE, zero can also mean nothing changed, so do not treat it alone as proof that the note is missing. Do not build a branch where the browser chooses which user's row to change. Once those checks pass, the app is ready for the next layer: a second table for comments, then search, then an API. That order is slower than bolting on features at random, but it keeps each new lesson attached to a boundary the reader already understands. Keep a short `README` beside the project with the database name, migration command, environment variables, and the routes you expect to work. Future you will be grateful, and future you is always less patient than present you expects. ## Common Pitfalls & Debugging ### A Write Changes the Wrong User's Note Symptom: changing a hidden note ID updates or deletes another account's row. Cause: the query filters by note ID alone and trusts the browser to supply ownership. Fix: load the authenticated user on the server, filter writes by both `id` and `user_id`, and check whether the note belongs to that user. An UPDATE can affect zero rows when the submitted values are unchanged. ### Saved Text Runs as HTML Symptom: a saved title changes the page markup or runs a script when the note list opens. Cause: stored text crossed into HTML without context-appropriate escaping. Fix: render every text value through `htmlspecialchars()` with UTF-8 and quote handling at the output boundary. ### Refreshing Creates a Duplicate Row Symptom: reloading after a successful form post inserts the same note again. Cause: the handler renders a response directly after the write, so the browser repeats the POST on refresh. Fix: return a `303` redirect to a GET route after the transaction succeeds. ## Frequently Asked Questions ### Can one PDO connection serve every request handler? A small PHP app can reuse one PDO connection during a request. Keep connection settings in one helper and pass the resulting object to query functions. Separate HTTP requests normally create their own connections. ### Does this PHP notes app prevent conflicting edits? This PHP notes app does not prevent conflicting edits. The last save wins when two tabs edit the same note. Add a version column and check it during UPDATE if you need to reject stale edits. ### Does moving a PHP app to a framework require new SQL queries? Moving a PHP app to a framework does not necessarily require new SQL queries. Existing PDO queries can often be retained, although connection management and where the functions live may change. ### Can a MySQL trigger stop a note from changing owners? A MySQL trigger can reject an UPDATE that changes a note’s owner. Application permission checks are still needed to decide who may edit the note. ### Should a PHP database connection use default production credentials? A production PHP database connection should fail clearly when required configuration is missing. Local defaults can be convenient during development, but they should not silently select a different production database. ### What do affected-row counts mean for PHP note updates and deletes? MySQL normally counts changed rows for UPDATE and deleted rows for DELETE. In this PHP app, updateNote() returns true only when data changes; false can also mean the submitted values were already saved. deleteNote() returns false when no row matches the note and owner. ### Does this PHP notes app change its CSRF token on every page load? This PHP notes app reuses its session CSRF token across page loads. The token changes when the session ends or the application deliberately regenerates it. Changing it on every page load would invalidate forms open in other tabs. ## When Should a PHP App Add a Framework? A PHP app benefits from a framework when routing, validation, sessions, and error handling become repetitive: - You need routing beyond a few paths.- You need middleware for sessions, auth, CSRF, and permissions.- You need migrations and environment-aware config.- You need tests around requests.- You need a team to understand the project quickly. Until then, the small app can stay small. The danger is pretending small means casual. A small PHP app still needs prepared statements, password hashing, CSRF protection, output escaping, and a schema that matches the data. Keep learning from the plain version while the routing file remains readable. Move to a framework before every route requires another hand-built copy of validation, CSRF, layout, middleware, and error handling. ## Self-Check Predict the output from this ownership check: ```php Php A small chat app combines [PHP](/programming/php/), MySQL, and JavaScript. PHP checks who can use each room and saves messages. JavaScript fetches new messages without reloading the page. This tutorial builds on an existing login system and PDO connection. You will need PHP with `pdo_mysql` and `mbstring`, a MySQL database, and a `users` table with an unsigned BIGINT `id` and a `display_name`. The examples show the chat handlers, not a complete registration or login system. ## Guide Path Read this after [building a small PHP database app](/programming/php/mysql/dynamic-sites/) and before [using sockets in PHP](/programming/php/networking/sockets/). The chat app combines polling, login sessions, CSRF protection, and database queries. ## What Does a PHP Chat App Need? A PHP chat app needs a logged-in user, room membership checks, message storage, and endpoints for sending and reading messages. - A session-backed user identity. - A room or channel identifier. - A messages table with an index that supports recent reads. - A POST endpoint that validates and stores a message. - A GET endpoint that returns newer messages. - Escaped output in HTML and text-only insertion in JavaScript. - CSRF protection for every state-changing request. ## Start with the Schema Start with tables plain enough that the access pattern is visible. A room lets you avoid hard-coding one global conversation, and an indexed message stream keeps polling from turning into a full-table scan. ```sql CREATE TABLE chat_rooms ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, slug VARCHAR(100) NOT NULL UNIQUE, name VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE chat_room_members ( room_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, PRIMARY KEY (room_id, user_id), CONSTRAINT chat_members_room_fk FOREIGN KEY (room_id) REFERENCES chat_rooms (id) ON DELETE CASCADE, CONSTRAINT chat_members_user_fk FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ); CREATE TABLE chat_messages ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, room_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, body TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX chat_room_id_idx (room_id, id), CONSTRAINT chat_messages_room_fk FOREIGN KEY (room_id) REFERENCES chat_rooms (id) ON DELETE CASCADE, CONSTRAINT chat_messages_user_fk FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ); -- After creating room 1 and test user 1: INSERT INTO chat_room_members (room_id, user_id) VALUES (1, 1); ``` Use InnoDB so the foreign keys are enforced. The database should know that a message belongs to a room and a user. If that relationship lives only in PHP comments, a maintenance script or import job can create rows the application cannot explain. The index starts with `room_id`, then `id`, matching the query below. It helps MySQL find newer messages in one room without scanning every room. See [how SQL indexes support queries](/programming/sql/indexes/query-optimization/) for the reasoning. ## Insert Messages with PDO The write path should validate the body before SQL and use prepared statements for values. PDO placeholders carry values, which is all a chat form needs. ```php 1000) { throw new InvalidArgumentException("Keep messages to 1000 characters or fewer."); } $statement = $pdo->prepare( "INSERT INTO chat_messages (room_id, user_id, body) VALUES (:room_id, :user_id, :body)", ); $statement->execute([ "room_id" => $roomId, "user_id" => $userId, "body" => $body, ]); } ``` That function does not know about forms, templates, or redirects. It writes one message and leaves presentation to the request layer. The request handler can decide whether the message came from a normal form post, a fetch request, or a test. ## Protect the POST Endpoint Posting a chat message changes state, so it needs the same protection as any other form. A small app can use a session-backed CSRF token as long as the token is generated server-side and verified on POST. ```php prepare( "SELECT 1 FROM chat_room_members WHERE room_id = :room_id AND user_id = :user_id LIMIT 1", ); $statement->execute([ "room_id" => $roomId, "user_id" => $userId, ]); if (! $statement->fetchColumn()) { http_response_code(403); exit("Room access denied."); } } if ($_SERVER["REQUEST_METHOD"] === "POST") { verifyCsrfToken($_POST["csrf_token"] ?? ""); $pdo = db(); $userId = requireUserId(); // Placeholder: resolve the room from the route or validated query state. $roomId = currentRoomId(); requireRoomMember($pdo, $userId, $roomId); $body = $_POST["body"] ?? ""; if (! is_string($body)) { http_response_code(400); exit("Message must be text."); } createMessage($pdo, $roomId, $userId, $body); header("Location: /chat", true, 303); return; } ``` Do not use `GET` for posting messages into the room. Links, crawlers, browser previews, and prefetching should never be able to write to the database. The room ID is only a requested destination. `requireRoomMember()` checks the server-side guest list before the write, so changing a form value or URL cannot grant access to another room. Start the session in both request handlers before reading the logged-in user. Load your existing `db()` and `requireUserId()` helpers before these handlers. The [PHP PDO guide](/programming/php/mysql/pdo/) covers the connection; [PHP authentication and sessions](/programming/php/security/auth-sessions/) covers the logged-in user. Define `currentRoomId()` to validate the room ID from your route and reject missing or invalid IDs. Both handlers must resolve the same room. Include `csrfToken()` in the posting form as a hidden field. ## Escape Output at Render Time Store the message as text and escape it when rendering HTML. Validation decides what the app is willing to accept before storage. Escaping decides how the accepted text is safely represented in a specific output context. ```php
    1. ">
    ``` If you later add Markdown, emoji replacement, or linkification, do it through a real parser or an allowlisted sanitizer. Do not switch to raw `innerHTML` because the first version worked in a demo. ## Fetch Newer Messages The read endpoint should return messages after the last ID the browser has already displayed. This keeps each refresh small and gives the database an indexed path. ```php prepare( "SELECT m.id, u.display_name, m.body, m.created_at FROM chat_messages AS m JOIN users AS u ON u.id = m.user_id WHERE m.room_id = :room_id AND m.id > :after_id ORDER BY m.id ASC LIMIT 50", ); $statement->execute([ "room_id" => $roomId, "after_id" => $afterId, ]); return $statement->fetchAll(); } $pdo = db(); $userId = requireUserId(); $roomId = currentRoomId(); requireRoomMember($pdo, $userId, $roomId); header("Content-Type: application/json; charset=utf-8"); $afterId = filter_var($_GET["after_id"] ?? "0", FILTER_VALIDATE_INT, [ "options" => ["min_range" => 0], ]); if ($afterId === false) { http_response_code(400); exit(json_encode(["error" => "Invalid message ID."])); } echo json_encode([ "messages" => messagesSince($pdo, $roomId, $afterId), ]); ``` On the browser side, use `fetch()` and put message text into `textContent`, not `innerHTML`. ```js let lastId = Number(document.querySelector("[data-message-id]:last-child")?.dataset.messageId ?? 0); async function refreshMessages() { const url = "/chat/messages?after_id=" + encodeURIComponent(lastId); const response = await fetch(url, { headers: { Accept: "application/json" }, }); if (!response.ok) { return; } const payload = await response.json(); for (const message of payload.messages) { if (document.querySelector('[data-message-id="' + message.id + '"]')) continue; const item = document.createElement("li"); const author = document.createElement("strong"); const body = document.createElement("span"); item.dataset.messageId = String(message.id); author.textContent = String(message.display_name); body.textContent = String(message.body); item.append(author, body); document.querySelector(".chat-messages").append(item); lastId = message.id; } } async function pollMessages() { try { await refreshMessages(); } catch (error) { console.error("Could not load new messages.", error); } finally { setTimeout(pollMessages, 3000); } } pollMessages(); ``` The loop waits for each request to finish before scheduling the next, so slow requests cannot overlap. Adapt the URL to include your validated room route. This teaching example uses numeric IDs within JavaScript’s safe integer range; larger IDs need string handling. For a busy chat, also handle messages committed out of ID order, rate limits, and reconnecting clients before deployment. ## Should a PHP Chat App Use Polling, SSE, or WebSockets? A small PHP chat app can start with polling. SSE delivers server updates over a persistent connection; WebSockets support messages in both directions over one connection. Use polling when the chat is low volume, the app is small, and you want ordinary PHP request handling. A three-second interval is easy to debug, easy to throttle, and easy to cache around. If you want the refresh endpoint without the full chat project, read [smart auto refresh with PHP](/programming/php/browser/smart-auto-refresh/) for polling discipline, hidden-tab pauses, and the point where SSE or WebSockets become worth the extra machinery. Use Server-Sent Events when the server needs to push new messages to the browser and the browser does not need to send messages over the same connection. SSE is one-way from server to client, which fits many notification streams, but it asks more of your hosting model than a normal request. Be careful with long-lived PHP workers and connection limits before using it on busy pages. Use WebSockets when the conversation needs a persistent two-way connection, typing indicators, presence, or high-frequency events. At that point you are no longer writing a little PHP tutorial; you are operating a real-time service. That may be correct, but it is a different piece of infrastructure, and [using sockets in PHP](/programming/php/networking/sockets/) is the lower-level transport lesson beneath that choice. ## What Not to Copy from Old Mini Chats The old examples usually had the same problems: - Appending messages to one public text file. - Trusting a username from a hidden field. - Rendering stored messages without escaping. - Posting by query string. - Reading the entire history on every refresh. - Letting the browser refresh the whole page every few seconds. Those shortcuts were common because early PHP made them easy and shared hosting made everything else feel like overkill. They are the wrong defaults for a current PHP app. A mini chat can still be plain PHP, but it should use the same floor as any small app: sessions, CSRF, prepared statements, escaped output, and a database query that has a chance to stay fast. ## Common Pitfalls & Debugging ### A User Can Post into Another Room Symptom: changing a room ID in a request lets one account read or post in a different room. Cause: the handler trusts the requested ID without checking membership. Fix: load the authenticated user on the server and require room membership before both read and write queries. ### Message Markup Runs in the Page Symptom: a message changes page markup or executes a script. Cause: stored text was inserted through raw HTML or rendered without escaping. Fix: use `htmlspecialchars()` in PHP templates and `textContent` when JavaScript adds messages. ### Polling Repeats or Skips Messages Symptom: a refresh repeats old messages or misses rows with the same timestamp. Cause: the client uses time alone as its cursor or starts overlapping refreshes. Fix: query by the last increasing message ID, order by ID, and do not start a new request while the previous refresh is unfinished. ## Frequently Asked Questions ### Can polling display duplicate messages in a PHP chat app? Polling can display duplicate chat messages when requests overlap or the client inserts the same message ID twice. Two separate tabs do not duplicate each other’s page content. Wait for each request to finish and check message IDs before adding them. ### Should PHP chat polling slow down in an idle tab? PHP chat polling can slow down when no messages arrive and pause while the tab is hidden. Increasing the delay reduces unnecessary requests. This example keeps a fixed delay between completed requests to make the basic loop easier to follow. ### Should PHP chat messages use separate database rows? Store each PHP chat message in its own database row. Separate rows make pagination, sender filters, and message inserts straightforward. MySQL can query and index JSON data, but one growing JSON conversation complicates these routine operations. ### Can chat messages contain Markdown? Chat messages can contain Markdown if the app parses it and sanitizes the resulting HTML before display. Plain text is simpler to handle safely. Assigning untrusted HTML to innerHTML can allow malicious markup to run. ### Should a chat message list auto-scroll when new messages arrive? A chat message list should auto-scroll only when the reader is already at the bottom. Keep the scroll position unchanged while someone reads older messages, and show a new-message indicator instead. ## Self-Check Predict the output from this room-access helper: ```php Php A PHP news page stores posts and comments in separate MySQL tables. Each comment belongs to a post and stays hidden until a moderator approves it. This [PHP tutorial](/programming/php/) adds comments to an existing database app. You will need a PDO connection, `mbstring`, session-based CSRF helpers, and a moderator login. The examples below cover the queries and handlers; they do not include a complete login system or admin interface. ## Guide Path Read [Build Dynamic Sites With PHP and MySQL](/programming/php/mysql/dynamic-sites/) first if the database connection and request handlers are not in place yet. After this step, add [search](/programming/php/mysql/search-application/) across the news content. ## Create the Tables Keep posts and comments in separate tables because comments depend on posts, and the database should know that relationship. ```sql CREATE TABLE news_posts ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, slug VARCHAR(255) NOT NULL UNIQUE, body TEXT NOT NULL, published_at DATETIME NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE news_comments ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, post_id BIGINT UNSIGNED NOT NULL, display_name VARCHAR(120) NOT NULL, body TEXT NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'pending', created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX news_comments_post_created_idx (post_id, created_at), CONSTRAINT news_comments_post_fk FOREIGN KEY (post_id) REFERENCES news_posts (id) ON DELETE CASCADE ); ``` Use InnoDB for both tables so MySQL enforces the foreign key. Store dates consistently, for example in UTC, so the publication filter uses the same timezone as your saved timestamps. ## Fetch a Post with Its Comments Avoid joining everything together on the first pass. Fetch the post, then fetch the comments that belong to it. The code stays plain, and the app can enforce a moderation filter without hiding it in template logic. ```php prepare( "SELECT id, title, body, published_at FROM news_posts WHERE slug = :slug AND published_at <= CURRENT_TIMESTAMP LIMIT 1", ); $statement->execute(["slug" => $slug]); $post = $statement->fetch(PDO::FETCH_ASSOC); if (! $post) { throw new RuntimeException("Post not found."); } return $post; } function approvedCommentsForPost(PDO $pdo, int $postId): array { $statement = $pdo->prepare( "SELECT display_name, body, created_at FROM news_comments WHERE post_id = :post_id AND status = 'approved' ORDER BY created_at ASC, id ASC", ); $statement->execute(["post_id" => $postId]); return $statement->fetchAll(PDO::FETCH_ASSOC); } ``` One function fetches the published post. The other fetches only its approved comments. Add pagination when a post has too many comments to load at once. ## Validate Comment Input Check the name and comment length on the server, verify the CSRF token, and save new comments as pending. ```php 120) { throw new InvalidArgumentException("Use a display name of 1 to 120 characters."); } if ($body === "" || mb_strlen($body) > 2000) { throw new InvalidArgumentException("Write a comment of 1 to 2000 characters."); } $statement = $pdo->prepare( "INSERT INTO news_comments (post_id, display_name, body, status) VALUES (:post_id, :display_name, :body, 'pending')", ); $statement->execute([ "post_id" => $postId, "display_name" => $name, "body" => $body, ]); } ``` A pending comment is saved but not displayed publicly. A moderator can review it before approval. The request handler should derive the post from a published slug and verify the form token before the prepared insert runs: ```php $_POST["display_name"] ?? "", "body" => $_POST["body"] ?? "", ]; $errors = []; try { verifyCsrfToken($_POST["csrf_token"] ?? ""); createComment( $pdo, (int) $post["id"], $old["display_name"], $old["body"], ); header("Location: /news/" . rawurlencode($_POST["post_slug"] ?? ""), true, 303); return; } catch (PDOException $exception) { http_response_code(500); exit("The comment could not be saved. Please try again later."); } catch (InvalidArgumentException | RuntimeException $exception) { $errors["comment"] = $exception->getMessage(); $comments = approvedCommentsForPost($pdo, (int) $post["id"]); require __DIR__ . "/../views/news-show.php"; return; } ``` The browser can request a slug, but it cannot choose an unpublished row or set the moderation status. Validation and token failures now return to the form through the same `$errors` and escaped old-input pattern used in step 1 instead of becoming an unhandled 500 response. ## Who Can Approve Comments in a PHP App? Only an authenticated moderator should approve comments in a PHP app. Check the moderator’s role and CSRF token on the server before updating the comment, even when readers can comment anonymously. ```php prepare( "UPDATE news_comments SET status = 'approved' WHERE id = :id AND status = 'pending'", ); $statement->execute(["id" => $commentId]); return $statement->rowCount() === 1; } // In a POST-only route, after starting the session and loading your helpers: requireModerator(); $token = $_POST["csrf_token"] ?? null; $commentId = filter_var($_POST["comment_id"] ?? null, FILTER_VALIDATE_INT, [ "options" => ["min_range" => 1], ]); if (! is_string($token) || $commentId === false) { http_response_code(400); exit("Invalid approval request."); } verifyCsrfToken($token); approveComment(db(), $commentId); ``` Implement `requireModerator()` in your application to reject anyone without the moderator role. Start with the [PHP authentication and sessions guide](/programming/php/security/auth-sessions/), then add the role check. Load that helper, `db()`, and `verifyCsrfToken()` before running this handler. The browser supplies a comment ID, but cannot choose its new status. ## Escape on Output Store the submitted text, then escape it for the HTML context where it appears. ```php

    ``` Avoid switching to raw HTML because one trusted user wants formatting. If comments need Markdown, add a parser and a sanitizer that you understand. The storage rule and the output rule are separate decisions. ## Common Pitfalls & Debugging ### Pending Comments Appear on the Public Page Symptom: a newly submitted comment appears before a moderator approves it. Cause: the public read query selects every child row for the post. Fix: keep `status = 'approved'` in the prepared public query and use a separate authorized query for the moderation screen. ### A Comment Is Attached to the Wrong Post Symptom: changing a hidden post ID attaches a comment to another or unpublished article. Cause: the handler trusts the submitted parent ID. Fix: resolve a published post from the requested slug on the server, then pass that row's integer ID to the insert. ### Comment Text Changes the Page Markup Symptom: a display name or comment body adds elements or scripts to the page. Cause: stored text is rendered as HTML. Fix: pass each value through `htmlspecialchars()` and apply `nl2br()` only after escaping the body. ## Frequently Asked Questions ### Why use separate post and comment tables? A post and a comment have different fields, permissions, and lifecycles. Separate tables let one post own many comments through a foreign key, while each query can select only the columns and moderation state its screen needs. ### Can a site accept anonymous comments? A PHP site can accept anonymous comments without requiring accounts. Validate submissions, verify CSRF tokens, limit repeated requests, moderate comments, and escape displayed text. A login helps identify a commenter but does not replace those protections. ### Should comments be deleted with their post? Comments can be deleted automatically with their post by using ON DELETE CASCADE. Keep separate records instead if your moderation or retention policy requires them to survive the post’s removal. ### When should comments support Markdown? Comments need Markdown only when readers need formatting such as lists or code blocks. Parse Markdown and sanitize the generated HTML before displaying it. Plain-text comments are simpler to handle safely. ### Can a comment be edited after approval? An approved comment can be edited if the application provides an edit handler and checks who owns the comment. Return changed text to pending review when edits require moderation. This tutorial does not implement editing. ### Should the post list show a comment count? A news post list can show the number of approved comments for each post. Use a grouped count or a maintained counter instead of running one extra query per post. Exclude pending comments from the public count. --- ## PHP PDO Guide: MySQL Queries and Prepared Statements URL: /programming/php/mysql/pdo/ Cluster: Programming > Php PDO gives [plain PHP](/programming/php/) one connection and statement interface for database work. With MySQL, it keeps the SQL template separate from request values through prepared statements. PDO uses a database-specific driver, so MySQL requires pdo_mysql. The examples assume an existing app database, the shown tables and columns, and credentials loaded into $_ENV. PDO shares an API across databases; it does not make MySQL-specific SQL portable. ## How Do You Connect PHP to MySQL with PDO? Create a PDO connection with a MySQL DSN, credentials, and connection options. Then prepare the SQL and pass the input value to execute(). Here, $email is an already validated email address. ```php PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ], ); $statement = $pdo->prepare( "SELECT id, email, created_at FROM users WHERE email = :email LIMIT 1", ); $statement->execute([ "email" => $email, ]); $user = $statement->fetch(); ``` The SQL template and the values are separate. PHP's PDO documentation is explicit here. Parameter markers are for values, and prepared statements help prevent SQL injection by removing the need to manually quote and escape those values. The connection options control errors, returned rows, and how statements are prepared. `PDO::ERRMODE_EXCEPTION` makes query failures visible instead of letting the page continue with a false result. `PDO::FETCH_ASSOC` returns rows keyed by column name, and `PDO::ATTR_EMULATE_PREPARES` set to `false` asks the MySQL driver to use native prepared statements. The `charset=utf8mb4` fragment decides character handling at connection time. PHP 8 defaults PDO to exception mode, but setting it explicitly still pays for itself. It documents the expectation for the next maintainer, keeps older deployments from behaving differently, and gives tests a clean failure when a query or table name is wrong. Silent database errors were tolerable in throwaway scripts. In a database-backed app, they turn one mistake into a second mistake because the page keeps making decisions with missing data. PDO parameters represent complete values, not table names, column names, keywords, or arbitrary SQL fragments. If the user can choose a sort column, use an allowlist in PHP, then insert the already-approved column name. ```php "created_at", "email" => "email", ]; $requestedSort = $_GET["sort"] ?? "created"; $sort = is_string($requestedSort) ? ($allowedSorts[$requestedSort] ?? "created_at") : "created_at"; $statement = $pdo->prepare( "SELECT id, email, created_at FROM users ORDER BY {$sort} DESC LIMIT 25", ); $statement->execute(); ``` The variable there is safe only because it came from your own map. The same rule applies to table names, directions, and optional filter fragments. `ASC` or `DESC` should come from a small map. A table switch should come from application logic, not `$_GET["table"]`. If the query shape changes based on user input, the part you insert into the SQL string needs to be something your code selected from known values. When the SQL needs an integer, bind it as an integer. MySQL may convert numeric strings, but explicit integer binding makes parameters such as LIMIT unambiguous. ```php prepare( "SELECT id, email, created_at FROM users ORDER BY created_at DESC LIMIT :limit OFFSET :offset", ); $statement->bindValue("limit", 25, PDO::PARAM_INT); $statement->bindValue("offset", 50, PDO::PARAM_INT); $statement->execute(); $users = $statement->fetchAll(); ``` MySQL reports changed rows for an `UPDATE` by default. Setting a column to its current value can therefore make `rowCount()` return zero even when the `WHERE` clause matched a row. Zero alone cannot distinguish an idempotent save from a missing record. Existing PDO MySQL code can request matched-row behavior with `PDO::MYSQL_ATTR_FOUND_ROWS`. On PHP 8.4 and newer, the dedicated `Pdo\Mysql` subclass provides the same setting as `Pdo\Mysql::ATTR_FOUND_ROWS`. Choose that connection behavior deliberately because it changes what update counts mean across the application. ```php prepare( "UPDATE users SET display_name = :display_name WHERE id = :id", ); $statement->bindValue("display_name", $displayName); $statement->bindValue("id", $userId, PDO::PARAM_INT); $statement->execute(); if ($statement->rowCount() > 1) { throw new RuntimeException("Expected at most one user row to change."); } ``` This check accepts zero changed rows, so saving an unchanged display name does not throw. If the application must distinguish an unchanged row from a missing user, enable matched-row behavior for the connection or check existence separately. ## Reuse the PDO Connection Put connection setup in one place instead of copying the DSN and attributes into every page. Older PHP applications often scattered connection calls across files, which made a hostname, credential, or error-mode change easy to miss. ```php PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ], ); return $pdo; } ``` Laravel and Symfony wrap this work in their own layers, and the underlying boundary still matters. When something fails, you want to know where the connection is made, how errors surface, and which code owns the query. Keep credentials out of the PHP file for the same reason. A local `.env` loader, server-level environment variables, or your host's secret store are all better than committing a username and password beside the code that uses them. The PDO example reads `$_ENV` because the connection helper should know how to connect, while the source file should not become the place where production secrets live forever. ## How Do You Fetch Rows with PDO? Use `fetch()` when the query should return one row, `fetchAll()` when the result set is intentionally small, and iteration when the result might grow. A lookup by email, slug, or primary key should read like a single-row operation. A dashboard table can usually fetch a page of rows. A report export should not pull every matching row into memory before writing the first line. ```php prepare( "SELECT id, email, created_at FROM users WHERE active = 1 ORDER BY created_at DESC LIMIT :limit", ); $statement->bindValue("limit", 100, PDO::PARAM_INT); $statement->execute(); foreach ($statement as $user) { echo htmlspecialchars($user["email"], ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); } ``` Iteration avoids building another array with fetchAll(), but PDO MySQL buffers results by default. For large exports, use pagination or an unbuffered query. An unbuffered result must be consumed or closed before another query uses that connection. There is another small habit worth keeping. Handle the "no row" case where it happens. `fetch()` returns `false` when there are no more rows, so a lookup should branch before the template assumes an array exists. ```php prepare( "SELECT id, email, created_at FROM users WHERE id = :id LIMIT 1", ); $statement->bindValue("id", $userId, PDO::PARAM_INT); $statement->execute(); $user = $statement->fetch(); if ($user === false) { http_response_code(404); exit("User not found"); } ``` That branch turns a missing row into an intentional 404 instead of a notice-filled page that leaks implementation detail. ## Select Only the Columns You Need `SELECT *` returns every column, including data the page may not need. Naming columns reduces unnecessary transfer and makes the expected result easier to see. Prefer naming the columns the page renders: ```sql SELECT id, email, created_at FROM users WHERE active = 1 ORDER BY created_at DESC LIMIT 25; ``` The loose version below is easier to type and harder to maintain: ```sql SELECT * FROM users; ``` When the query starts joining tables, explicit columns become even more important. The [SQL joins guide](/programming/sql/joins/) covers the join side of that problem. ## When Should PDO Use a Transaction? PDO should use a transaction when related writes must succeed or fail together. With MySQL, use transactional tables such as InnoDB. ```php beginTransaction(); try { $order = $pdo->prepare( "INSERT INTO orders (user_id, total_cents) VALUES (:user_id, :total_cents)", ); $order->execute([ "user_id" => $userId, "total_cents" => $totalCents, ]); $orderId = (int) $pdo->lastInsertId(); $item = $pdo->prepare( "INSERT INTO order_items (order_id, sku, quantity) VALUES (:order_id, :sku, :quantity)", ); $item->execute([ "order_id" => $orderId, "sku" => $sku, "quantity" => $quantity, ]); $pdo->commit(); } catch (Throwable $error) { if ($pdo->inTransaction()) { $pdo->rollBack(); } throw $error; } ``` The database should not be left with an order but no order item because PHP hit an exception halfway through. Read [SQL transactions and ACID](/programming/sql/transactions/acid/) before you build anything that moves money, inventory, permissions, or account state. Payment flows are only the obvious transaction example. Use the same pattern anywhere one user action has to leave the database in one coherent state: creating an account and profile, recording a moderation decision and audit row, or moving inventory between two locations. If the second write failing would make the first write misleading, wrap the pair. Do not let the transaction block become a hiding place for unrelated work. Validate the request before `beginTransaction()`, keep slow remote calls outside the transaction when you can, and commit as soon as the related database writes have succeeded. Also keep schema changes out of normal transaction examples. The PHP manual calls out that databases such as MySQL can implicitly commit around DDL statements like `CREATE TABLE` or `DROP TABLE`. That is a deployment and migration concern, separate from wrapping an order insert and its order items. Application transactions should mostly cover data changes that belong to one user action. ## Common Pitfalls & Debugging ### Emulated and Native Prepares Behave Differently Symptom: invalid SQL is not reported until `execute()`, or placeholder parsing changes after deployment. Cause: PDO MySQL uses emulated prepares by default, so PDO parses the statement instead of asking MySQL to prepare it. Fix: set `PDO::ATTR_EMULATE_PREPARES` deliberately, test the chosen mode, and use the placeholder style supported by the driver. ### The Error Mode Hides the Failure Symptom: a failed query returns `false` and later code reports a misleading secondary error. Cause: the connection uses silent error handling on an older deployment or overrides the PHP 8 default. Fix: set `PDO::ATTR_ERRMODE` to `PDO::ERRMODE_EXCEPTION` in the connection options and catch exceptions only where the application can respond meaningfully. ## Do PDO Prepared Statements Replace Validation? PDO prepared statements do not replace validation or permission checks. Check the email format, allowed price range, and the user’s permission before running the query. Escape returned values when displaying them in HTML. For form-heavy pages, pair this guide with [form validation in PHP](/programming/php/forms/spelling-validation/) and [PHP security fundamentals](/programming/php/security/fundamentals/). PDO is one layer of the defense. ## Frequently Asked Questions ### What is the difference between PDO exec and query? PDO exec() returns an affected-row count, while PDO query() returns a PDOStatement from which you can fetch results. Neither method accepts bound parameters. Use prepare() and execute() when the query contains input values. ### Do prepared statements make PHP queries faster? Prepared statements can reduce repeated parsing when the same SQL template runs many times, but they are not a guaranteed speed improvement for one query. Their main benefit is keeping values separate from SQL. Measure repeated workloads instead of choosing them as a blanket optimization. ### What does PDO FETCH_ASSOC change compared with FETCH_BOTH? PDO FETCH_ASSOC returns each row with column names as keys. The default FETCH_BOTH also includes numeric keys. FETCH_ASSOC avoids those extra array entries, but it does not guarantee a specific memory saving. ### Does closing a PDO connection roll back an open transaction? PDO automatically rolls back an outstanding transaction started with beginTransaction() when the script ends or the connection closes. This requires transactional tables and cannot undo changes already committed, including MySQL implicit commits around some schema statements. ### Can you bind an array of values to a single PDO placeholder for an IN clause? PDO cannot bind an array to one IN-clause placeholder. Generate one placeholder per value and pass the values separately to execute(). Handle an empty array before building the query. ### Does PDO automatically retry a query if the database connection drops mid-request? PDO does not automatically retry a failed query. With exception mode enabled, the application must catch the connection error and decide whether retrying is safe. A lost response does not prove that a write failed, so retrying it can create duplicate records. ### Is PDO quote a safe substitute for a prepared statement? PDO quote() can quote a string value for a supported driver, but prepared statements are easier to use consistently. quote() cannot safely substitute table names, column names, or arbitrary SQL fragments. ## Self-Check 1. Which value can a PDO parameter represent: an email address, a column name, or the keyword `DESC`? 2. What column does the allowlist example select when `$_GET["sort"]` contains an unknown key? 3. What can a MySQL `rowCount()` result of zero mean after an idempotent update? 4. What response does the single-user lookup produce when `fetch()` returns `false`? 5. Where should request validation happen: before or after `beginTransaction()`? ### Answers 1. An email address. Parameters represent complete data values, while identifiers and keywords need an application allowlist. 2. `created_at`. The second fallback selects the known default when the requested key is absent from the map. 3. The row was unchanged or absent. Default MySQL changed-row counts cannot distinguish those outcomes. 4. HTTP 404 with `User not found`. The branch sets the status before stopping the script. 5. Before the transaction. Validation should finish before the database begins holding transactional resources. ## Build a Small Database-Backed Page PDO handles connections and queries. You still need appropriate tables, indexes, validation, and transaction rules. The [PHP and MySQL dynamic-site guide](/programming/php/mysql/dynamic-sites/) puts the connection, query, and HTML output into one small application. --- ## Add Search to a PHP and MySQL App URL: /programming/php/mysql/search-application/ Cluster: Programming > Php A [PHP](/programming/php/) search form sends a query to MySQL and displays matching articles. Start with `LIKE` for a small dataset. Use a full-text index when readers need word matching and relevance scores. This tutorial needs PHP with `pdo_mysql` and `mbstring`, a MySQL database, and the PDO connection from the dynamic-site guide. It uses a new `articles` table rather than the previous tutorial’s `news_posts` table. Keep the database connection and saved publication times in the same timezone. ## Guide Path Follow this step after [news and comments CRUD](/programming/php/mysql/news-comments/). After search works, the path pauses at the [authentication and sessions reference](/programming/php/security/auth-sessions/), then finishes with the [custom API capstone](/programming/php/apis/custom-api/). ## Create the Articles Table Store each article’s title, summary, body, URL slug, and publication time in one table. ```sql CREATE TABLE articles ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, slug VARCHAR(255) NOT NULL UNIQUE, summary TEXT NOT NULL, body MEDIUMTEXT NOT NULL, published_at DATETIME NOT NULL, INDEX articles_published_idx (published_at, id) ); ``` The publication time filters out future articles. The ID breaks ties when several articles share a timestamp. The [PHP dynamic-site guide](/programming/php/mysql/dynamic-sites/) explains how to connect to the database and separate queries from HTML templates. ## How Do You Search MySQL from PHP? Search MySQL from PHP with a prepared SELECT statement, binding the search text as a value. This LIKE query matches part of a title or summary and returns only published articles. ```sql SELECT id, title, slug, summary, published_at FROM articles WHERE ( title LIKE :title_term ESCAPE '!' OR summary LIKE :summary_term ESCAPE '!' ) AND published_at <= CURRENT_TIMESTAMP ORDER BY published_at DESC, id DESC LIMIT 20 OFFSET 0; ``` The PHP side should still use a prepared statement: ```php 100) { throw new InvalidArgumentException("Keep searches to 100 characters or fewer."); } $page = min(1000, max(1, $page)); $limit = 20; $offset = ($page - 1) * $limit; $term = "%" . escapeLike($query) . "%"; $statement = $pdo->prepare( "SELECT id, title, slug, summary, published_at FROM articles WHERE ( title LIKE :title_term ESCAPE '!' OR summary LIKE :summary_term ESCAPE '!' ) AND published_at <= CURRENT_TIMESTAMP ORDER BY published_at DESC, id DESC LIMIT :limit OFFSET :offset" ); $statement->bindValue("title_term", $term); $statement->bindValue("summary_term", $term); $statement->bindValue("limit", $limit, PDO::PARAM_INT); $statement->bindValue("offset", $offset, PDO::PARAM_INT); $statement->execute(); return $statement->fetchAll(); } ``` Binding `LIMIT` and `OFFSET` as integers relies on the [step 1 PDO connection](/programming/php/mysql/dynamic-sites/#connection-file) keeping `PDO::ATTR_EMULATE_PREPARES` set to `false`. Bind the search text as a value instead of inserting it into the SQL string. `LIKE` has its own small trap: `%` and `_` are wildcard characters, so a user searching for `%` should not accidentally ask for every row. Escape those characters before building the pattern: ```php ["min_range" => 1, "max_range" => 1000], ]); if (! is_string($query) || mb_strlen($query) > 100 || $page === false) { http_response_code(400); exit("Invalid search or page number."); } $results = searchArticles(db(), $query, $page); ``` ```php

    ">

    ``` Show a clear “No matching articles” message when a nonempty search returns no rows. The form uses GET so readers can bookmark or share a search. ## Common Pitfalls & Debugging ### A Percent Search Returns Every Row Symptom: searching for `%` matches the complete article table. Cause: the application wraps raw input in wildcards without escaping LIKE's special characters. Fix: escape `!`, `%`, and `_` in the value, then declare the matching `ESCAPE '!'` clause. ### Draft Articles Appear in Public Search Symptom: a public result links to an unpublished or restricted row. Cause: the search query filters text but omits the publication or authorization predicate. Fix: keep the visibility condition inside every public LIKE and FULLTEXT query. ### A Result Title Changes the Page Markup Symptom: a stored title inserts elements or scripts into the result page. Cause: the template treats database text as trusted HTML. Fix: apply `htmlspecialchars()` to titles and summaries, and use `rawurlencode()` for the slug path segment. ## Frequently Asked Questions ### Can a MySQL trailing-wildcard search use an index? A MySQL LIKE search with a trailing wildcard, such as database%, can use a B-tree index to narrow the matching range. A pattern starting with %, such as %database%, usually cannot use that range lookup. Check the query plan rather than assuming MySQL will choose an index. ### Does MySQL full-text search match plurals automatically? MySQL’s built-in full-text parser does not automatically reduce words to a shared root. Searching for cat does not inherently match cats. Natural-language search is not the same as stemming or fuzzy matching. ### Should the search box use type search or type text? Use input type search for a search box. It identifies the field’s purpose, and some browsers add a clear button. PHP receives the submitted value in the same way as text input. ### Should public search require sign-in? Public article search does not need sign-in. Its query must exclude unpublished or restricted articles. Private search requires authentication and filters that limit results to records the user may view. ### Should search results update as the user types, or only after the form is submitted? A server-rendered PHP search can update after form submission, giving readers a bookmarkable results page. Live search needs JavaScript to delay requests while someone types and ignore stale responses. Add it only if that interaction helps your readers. --- ## PHP Sockets: TCP Connections, Reads, and Timeouts URL: /programming/php/networking/sockets/ Cluster: Programming > Php Most [PHP applications](/programming/php/) should not hand-write HTTP requests over raw sockets. Use an HTTP client, cURL, a provider SDK, or framework tooling for ordinary web API calls. Use sockets when the protocol is the point: WHOIS, Redis-style line protocols, a private TCP service, a Unix socket, a UDP message, or a long-running service that really owns its event loop. Think of a socket as an open phone line. The protocol is the agreed script, each write speaks part of it, each read waits for the reply, and the timeout is the rule for hanging up. Leaving a learning exercise in a real request path means the whole page can wait on that line. Sockets expose timeouts, blocking reads, TLS choices, partial writes, connection limits, and errors that an HTTP library normally owns. One careless blocking call can hold a PHP worker until another server responds. ## Guide Path Read this after [building a mini chat app with PHP](/programming/php/mysql/mini-chat/) if you need to understand the lower transport layer underneath polling, SSE, WebSockets, and custom protocols. Use [build your own API with PHP](/programming/php/apis/custom-api/) when the contract should stay at HTTP and JSON instead. ## Should PHP Use Streams or the Sockets Extension? Start with PHP streams for ordinary TCP clients. Use the sockets extension when you need lower-level socket options that streams do not expose: - Streams generalize readable and writable resources: files, network connections, compression wrappers, and protocol wrappers. - The sockets extension exposes a lower-level BSD sockets interface for client and server work. For most client-side network work, start with streams. PHP's own sockets manual points readers toward `stream_socket_client()`, `stream_socket_server()`, `fsockopen()`, and `pfsockopen()` for a more generic client-side socket interface, which is the practical hint. `fsockopen()` still exists and still opens Internet or Unix domain socket connections. `stream_socket_client()` gives you the same basic shape with richer options, including contexts and asynchronous connection flags. ## How Do You Connect to a TCP Server with PHP? Connect to a TCP server with `stream_socket_client()`, set a read timeout, send the protocol request, read the reply, and close the connection. ```php Php [Object-oriented PHP](/programming/php/) keeps related data and behavior inside named types. A class can describe an article, protect its valid state, and expose the operations the rest of an application is allowed to perform. Properties store an object’s data, methods work with that data, and a constructor sets up the object. Visibility controls which members other code can access. Inheritance builds on an existing class; interfaces define requirements that different classes can implement. The PHP 4 to PHP 5 change remains useful dated history. PHP 5 was released in July 2004 with Zend Engine 2 and a new object model. Assigning a PHP object to another variable copies its object identifier, so both variables access the same object. This is different from creating a PHP reference with `&`; use `clone` when you need a separate object. The [Zeev Suraski profile](/interviews/01-zeev-suraski-php/) provides the engine history behind that shift. Use this after [PHP control flow](/programming/php/control-flow/) and before you start turning every application boundary into a class. If the app is already fetching rows, rendering lists, and paginating results, OOP helps you name the boundary instead of passing loose arrays through every function. ## What Are Classes and Objects in PHP? A PHP class defines a type’s properties and methods. An object is an instance of that class, created with `new`. The examples gradually replace the `Article` definition; do not paste every version into one file. Examples using `mb_substr()` require the mbstring extension. ```php body), 0, $length); } } $article = new Article(); $article->title = "PHP classes"; $article->body = "

    Properties store state.

    "; echo $article->excerpt(); ``` The final `echo` prints `Properties store state.` after `strip_tags()` removes the paragraph tags. Tag removal is not a security sanitizer; escape the excerpt when displaying it in HTML. The class now has two string properties and an excerpt method. The `$this` variable refers to the object whose method is running, so each article produces an excerpt from its own body. The empty defaults expose the next problem. Outside code can create an article and forget to supply a title or body, leaving an object that exists but is not ready to use. ## Add a Constructor A constructor checks the required parts when the object is assembled. Constructor property promotion, available since PHP 8.0, declares and assigns the properties from the parameter list. The `readonly` modifier used for the ID requires PHP 8.1 or newer. ```php body), 0, $length); } } $article = new Article( id: 42, title: "PHP classes", body: "

    Constructors establish valid state.

    ", ); ``` The object cannot be created without an integer ID, title, and body. The readonly ID also cannot be reassigned after construction. Constructors work best when they establish valid starting state instead of performing slow database or network work. ## Control Access with Visibility PHP visibility controls which code can access a property or method. Public members are available to calling code, private members belong to the class itself, and protected members are also available to child classes. ```php rename($title); } public function rename(string $title): void { if (trim($title) === "") { throw new InvalidArgumentException("An article needs a title."); } $this->title = $title; } public function title(): string { return $this->title; } public function excerpt(int $length = 160): string { return mb_substr(strip_tags($this->body), 0, $length); } } ``` Outside code can rename the article only through `rename()`, so the title rule cannot be bypassed by direct assignment. Keep a property public when unrestricted access is harmless. Use private state when every change must pass through a rule. ## Extend a Class with Inheritance PHP inheritance creates a child class from an existing parent class. Use it when the child truly preserves the parent's meaning and contract, because the two classes become tightly connected. ```php publishedAt <= new DateTimeImmutable(); } } ``` A published article still satisfies the behavior of `Article` and adds a publication date. Composition is the safer default when one class merely needs another service. A publishing action can receive an `ArticleRepository` and `Mailer` without extending either one. ## Set a Contract with an Interface A PHP interface declares requirements for implementing classes. In this example, each implementation must provide a public `toHtml()` method. PHP 8.4 and later also allow interfaces to require publicly readable or writable properties. ```php title, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); $url = htmlspecialchars($this->url, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); return "{$title}"; } } ``` This is polymorphism: calling code can depend on `Renderable` and accept an article card, navigation item, or another implementation with the same connector. Add an interface when alternate implementations or a test double are real requirements. A single class does not need an interface merely to look formal. The rendered card assumes a trusted URL. Escaping a URL for an HTML attribute does not make a `javascript:` URL safe. Validate the scheme or use application-generated relative URLs. ## Use Objects at the Boundaries Objects can keep database queries, validation and external API calls in separate, testable parts of an application. This is abstraction: calling code asks a repository for an article without needing to know the SQL behind it. ```php pdo->prepare( "SELECT id, title, body FROM articles WHERE id = :id AND published_at IS NOT NULL", ); $statement->execute(["id" => $id]); $row = $statement->fetch(PDO::FETCH_ASSOC); if (! $row) { return null; } return new Article( (int) $row["id"], $row["title"], $row["body"], ); } } ``` This repository gives the SQL a home and returns a named object, so templates do not have to remember the table shape. Read [PHP and MySQL with PDO](/programming/php/mysql/pdo/) before copying this pattern into a real database app. The object boundary helps only if the SQL boundary is still disciplined. ## Domain Objects A beginner mistake is making the object match the table exactly and then assuming the job is done. That can be fine for a small object with no behavior yet. A `comments` table and a `Comment` object may have nearly the same fields. The table stores persistence details, while the object describes the value the rest of the app works with. For example, a table may store a `status` column as a string: ```sql CREATE TABLE comments ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, article_id BIGINT UNSIGNED NOT NULL, author_name VARCHAR(120) NOT NULL, body TEXT NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'pending', created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); ``` The object can still turn that storage shape into a small decision: ```php status === "approved"; } } ``` OOP does not have to mean a giant domain model. A small method with a clear name can remove repeated string checks from templates, controllers, and API responses. Once that boundary exists, the repository can translate database rows into objects in one place: ```php */ public function pendingForArticle(int $articleId): array { $statement = $this->pdo->prepare( "SELECT id, article_id, author_name, body, status FROM comments WHERE article_id = :article_id AND status = 'pending' ORDER BY created_at DESC", ); $statement->execute(["article_id" => $articleId]); return array_map( fn (array $row) => new ModeratedComment( (int) $row["id"], (int) $row["article_id"], $row["author_name"], $row["body"], $row["status"], ), $statement->fetchAll(PDO::FETCH_ASSOC), ); } } ``` The repository boundary gives you a subtle benefit here. If the table changes from `author_name` to separate `first_name` and `last_name` columns, the rest of the app can keep working with `ModeratedComment`. The repository absorbs the storage change, and the template keeps asking the object for the value it needs. ## Traits Traits let PHP reuse methods across classes without creating an inheritance tree. They are tempting because the first win is immediate: copy the method once, include the trait everywhere, and move on. Use traits with restraint, mainly for small mechanical behavior that has no business identity of its own. ```php pdo`, `$this->user`, or `$this->config` is a future debugging session with your name already written on it. ## Namespaces and Autoloading The old way to organize a growing PHP app was a pile of `require` statements. That worked until the include order mattered, two files wanted the same class name, or a missing include produced a production error that only appeared on one route. Modern PHP code normally uses namespaces plus Composer autoloading. The [Composer guide](/programming/php/dependencies/composer/) is the full walkthrough, but the shape is worth seeing here because it changes how classes are named. ```php updatedAt = clone $this->updatedAt; } } $original = new ArticleDraft(new DateTime("2026-01-15")); $copy = clone $original; $copy->updatedAt->modify("+1 day"); echo $original->updatedAt->format("Y-m-d"), PHP_EOL; echo $copy->updatedAt->format("Y-m-d"), PHP_EOL; ``` ### Static State Hides Dependencies Tests pass alone but fail together, or a class reads configuration that no constructor supplied. Mutable static properties act as shared global state. Pass stateful collaborators through the constructor. Reserve static methods for operations that do not depend on hidden mutable state. ### Missing Type Declarations Delay Errors An invalid array or string travels through several methods before failing. Missing property, parameter, or return types let the wrong shape cross the object boundary. Declare the narrowest useful types and convert database rows at the repository boundary. ## Small App OOP For a small app, start with objects in the places that protect the rest of the code: 1. Value objects for email addresses, slugs, dates, and IDs when they carry rules. 2. Repositories for database queries. 3. Services for actions that combine several steps. 4. Controllers or route handlers that translate HTTP into those services. 5. Interfaces only when there are real alternate implementations. Configuration and simple view data can stay in arrays. Use OOP where a named type reduces repeated knowledge or protects a real rule. ## A Practical Build Order For a beginner project, add OOP in this order: 1. Start with procedural code that works and passes data cleanly. 2. Move repeated SQL into repository classes. 3. Turn rule-bearing values into small value objects. 4. Add service classes when one action combines several collaborators. 5. Add interfaces after a second implementation, test double, or queue boundary appears. 6. Reach for inheritance only when a stable parent really explains the child. The PHP cluster follows the same order: first you build the dynamic site, then you make the database boundary safer, then you expose that work through an API. ## Frequently Asked Questions ### What is the difference between a PHP class and an object? A PHP class defines the properties and methods for a type. An object is one instance created from that class, with its own property values. One Article class can therefore create many Article objects with different titles and bodies. ### Should every PHP file contain a class? Not every PHP file needs a class. Classes suit values and services that need their own state or behavior. A small route, configuration array, template, or focused function can remain procedural when wrapping it in a class would only add ceremony. ### Can a PHP interface provide a default method body? A PHP interface cannot provide a default method body. Use a trait to share method implementations or an abstract class to combine shared implementations with required methods. ### What does readonly do on a PHP property? A PHP readonly property cannot normally be reassigned after initialization, but a nested object can still change internally. The modifier has been available since PHP 8.1. PHP 8.3 added an exception allowing reinitialization inside __clone() when creating a clone. ### Can a PHP class implement more than one interface? A PHP class can implement multiple interfaces, separated by commas after implements, unlike extending a class, which only allows one parent. Each interface still requires the class to provide every method the interface lists. ### Can a promoted PHP constructor parameter have a default value? A promoted PHP constructor parameter can have a default value, just like an ordinary parameter. Omitting that argument initializes the promoted property with the parameter’s default. ### What happens if both a class and a trait it uses define a constructor? When a PHP class and its trait both define a constructor, the class’s constructor takes precedence. Trait precedence runs class over trait over inherited parent, and the insteadof and as operators resolve conflicts between two traits rather than between a class and a trait, so the trait's constructor stays unused unless it is aliased to another name. ### What does final prevent on a PHP class or method? A final PHP class cannot be extended, and a final method cannot be overridden in a child class. It is the opposite of the flexibility inheritance depends on, so it signals that no further specialization is intended. ## Self-Check 1. What does a class define, and what does `new` create? 2. Which visibility should protect a property that may change only through a validation method: public, protected, or private? 3. What does the first `Article::excerpt()` example output? 4. What two dates does the `ArticleDraft` clone example output? 5. Why is composition usually safer than inheritance for application services? ### Answers 1. A class defines a type. The `new` keyword creates one object from that definition. 2. Private visibility. Calling code must use the public method that enforces the rule. 3. `Properties store state.` The method strips the HTML tags before returning the body excerpt. 4. `2026-01-15` and `2026-01-16`. The nested date is cloned, so modifying the copy leaves the original unchanged. 5. Composition keeps collaborators replaceable. The service uses another object without inheriting its full contract and internal assumptions. ## Next Steps Use a class when it keeps related data and operations together or prevents invalid changes. Keep simple code as functions when an object would add little. If you are building the whole app from scratch, pair this guide with [building a small PHP database app](/programming/php/mysql/dynamic-sites/). If the next step is an HTTP endpoint, read [build your own API with PHP](/programming/php/apis/custom-api/). --- ## PHP Pagination with Previous and Next Links URL: /programming/php/pagination/ Cluster: Programming > Php Previous and next links look like a small [PHP problem](/programming/php/) until the database grows. Keep the database doing the slicing, validate the page number, bind the values through PDO, and treat deep pagination as a performance question. Passing a raw `start` offset around feels more direct than calculating one from a page number. It works until someone sends a negative value, skips the first page, or pastes the variable into SQL without thinking. The fix is simple, and it has to be deliberate. ## Guide Path Read [PHP control flow](/programming/php/control-flow/) first, because pagination is mostly input validation plus a database query. After this, the same pattern shows up in [the PHP search tutorial](/programming/php/mysql/search-application/), where the result set also needs a stable order and a safe page size. ## How Do You Validate a PHP Pagination Page Number? Validate the requested page as a positive integer before calculating its SQL offset. The browser URL should expose a page number because that is what people understand: ```text /articles?page=3 ``` Read it as an integer and clamp it to page one or higher: ```php [ "default" => 1, "min_range" => 1, "max_range" => 100000, ], ], ); $page = max(1, (int) $page); $perPage = 10; $offset = ($page - 1) * $perPage; ``` The upper bound prevents extreme offsets and integer overflow. Choose a limit appropriate to your app; malformed or out-of-range input falls back to page one in this example. ## How Do You Fetch One Page with PDO? Fetch one page with PDO by binding integer limits and offsets to a SQL query with a stable sort order. Do not fetch the full result set and slice it in PHP: ```sql SELECT id, title, slug, published_at FROM articles WHERE published_at <= NOW() ORDER BY published_at DESC, id DESC LIMIT :limit OFFSET :offset; ``` The example uses MySQL-compatible SQL and assumes `$pdo` is an existing PDO connection. Bind the integer values before executing: ```php prepare( "SELECT id, title, slug, published_at FROM articles WHERE published_at <= NOW() ORDER BY published_at DESC, id DESC LIMIT :limit OFFSET :offset", ); $statement->bindValue("limit", $perPage, PDO::PARAM_INT); $statement->bindValue("offset", $offset, PDO::PARAM_INT); $statement->execute(); $articles = $statement->fetchAll(); ``` A stable `ORDER BY` clause matters for predictable pagination. If rows share the same timestamp and the query has no secondary sort, page boundaries can shift while readers move through the list. If this query belongs to a search screen, read [building a search application with PHP and SQL](/programming/php/mysql/search-application/) next. Search pagination has the same mechanics, plus the extra cost of matching and ranking. ## How Do You Check Whether Another Page Exists? Check for another page by fetching one extra row, or count the matching rows when the interface needs a total. The first option is a separate count query: ```sql SELECT COUNT(*) AS total FROM articles WHERE published_at <= NOW(); ``` That gives you page counts and "Page 3 of 12" style UI, but the count can be expensive on larger filtered result sets. The second option fetches one extra row from the result: ```php bindValue("limit", $limit, PDO::PARAM_INT); $statement->bindValue("offset", $offset, PDO::PARAM_INT); $statement->execute(); $rows = $statement->fetchAll(); $hasNext = count($rows) > $perPage; $articles = array_slice($rows, 0, $perPage); ``` That is usually enough for previous and next links. You know whether another page exists without promising a total count that may be slow or stale. ## How Do You Build Previous and Next Links? Keep existing filters when changing the page number. `http_build_query()` will encode the query string for you: ```php 1): ?> Previous Next ``` Query-string encoding and HTML escaping do different jobs. Keep both, and accept only the filters your application supports. ## When Should You Use Keyset Pagination? Use keyset pagination for deep chronological lists, activity feeds, and large result sets where readers move forward from the last item. Offset pagination is fine for small and medium lists. The database still has to walk past skipped rows, though, so page 4,000 can be much more expensive than page 4. When readers need to move through deep chronological lists, use keyset pagination instead. The URL carries the last seen position rather than a page number: ```sql SELECT id, title, slug, published_at FROM articles WHERE published_at <= NOW() AND (published_at < :cursor_published_at OR (published_at = :cursor_same_time AND id < :cursor_id)) ORDER BY published_at DESC, id DESC LIMIT :limit; ``` Bind both timestamp placeholders to the last displayed row’s timestamp and `:cursor_id` to its ID. With a suitable index, this query can start after that row instead of scanning every earlier page. It is less friendly for jumping to page 27, but it is better for endless lists, activity feeds, and large result sets. The [SQL indexes and query optimization](/programming/sql/indexes/query-optimization/) guide covers why this works. Pagination is still a query-plan problem, even when the visible feature is just two links. ## Keep Pagination in the Query Pagination belongs close to the query because the database owns the order, filters, and row limits. PHP should validate the request, bind the values, render the links, and escape the output. That separation is the same one used in [PHP and MySQL with PDO](/programming/php/mysql/pdo/) and [building a small PHP database app](/programming/php/mysql/dynamic-sites/). The page link is a tiny UI feature, but the correctness lives in the database boundary underneath it. ## Frequently Asked Questions ### Does LIMIT and OFFSET work the same in every database? LIMIT and OFFSET are available in MySQL, PostgreSQL and SQLite, but not every database uses the same pagination syntax. SQL Server uses OFFSET and FETCH. Check syntax and query performance on the database you deploy. ### Should page numbers in a URL start at 0 or 1? Public pagination URLs usually start at page 1. Readers count from one, and a URL saying page=1 for the first page matches what a person expects. Convert to the zero-based offset in the query rather than exposing it in the address. ### What happens when someone requests a page past the last one? A pagination query with an offset beyond the last row returns an empty result rather than an error. Treat an empty result at a requested page as a normal case to handle, not a failure. ### Is deep pagination good for search engines? Paginated pages can be crawled when each page has its own URL and ordinary links connect the sequence. Use a self-referencing canonical for each page rather than pointing every page to page one. A sitemap can help discovery but does not replace crawlable navigation. --- ## PHP Authentication and Sessions: A Security Reference URL: /programming/php/security/auth-sessions/ Cluster: Programming > Php [PHP](/programming/php/) authentication checks which account is signing in. A session keeps that account signed in between requests, usually by storing a session ID in a browser cookie and the account data on the server. This guide explains password checks, session cookies, login guards, and logout. The snippets assume a PDO connection, validated email and password strings, and a users table with a unique email and password_hash column. For production account recovery, MFA, or SSO, use a maintained authentication framework or identity provider rather than building every feature yourself. ## How Should PHP Store and Check Passwords? PHP should store passwords with `password_hash()` and check login attempts with `password_verify()`. Password hashing makes stolen passwords harder to recover; it does not make a leaked database harmless. Do not store plaintext passwords or use fast general-purpose hashes such as MD5 or SHA-1. ```php prepare( "INSERT INTO users (email, password_hash) VALUES (:email, :password_hash)", ); $statement->execute([ "email" => $email, "password_hash" => $hash, ]); ``` The generated hash includes its algorithm, cost, and salt. A VARCHAR(255) column leaves room for future hash formats. If the selected algorithm is bcrypt, enforce its 72-byte input limit rather than silently truncating longer passwords. After a successful login, use password_needs_rehash() when changing your algorithm or cost. ```php prepare( "SELECT id, email, password_hash FROM users WHERE email = :email", ); $statement->execute(["email" => $email]); $user = $statement->fetch(PDO::FETCH_ASSOC); if (! $user || ! password_verify($plainPassword, $user["password_hash"])) { throw new RuntimeException("Invalid email or password"); } ``` Return the same error for a missing account and a wrong password every single time. The server can log the difference for its own triage, but a browser that learns which email addresses exist has just been handed a free enumeration tool. ## How Do You Configure PHP Session Cookies? Configure PHP session cookies before calling `session_start()`. Use HTTPS, strict mode, cookie-only session IDs, and Secure, HttpOnly, and SameSite cookie attributes. ```php 0, "path" => "/", "secure" => true, "httponly" => true, "samesite" => "Lax", ]); session_start(); ``` A lifetime of zero creates a session cookie, but browsers may restore it when reopening a previous session. Enforce expiry on the server too. Secure restricts transmission to HTTPS; HttpOnly prevents JavaScript from reading the cookie, but does not stop injected scripts from making requests. SameSite=Lax helps with cross-site requests but does not replace CSRF tokens. Strict mode rejects uninitialized IDs when the session handler supports validation. ## When Should PHP Regenerate the Session ID? Regenerate the PHP session ID after a successful login, before storing the authenticated user ID. Also rotate it when privileges increase. This prevents a known pre-login ID from gaining authenticated access. ```php prepare( "SELECT id, email FROM users WHERE id = :id", ); $statement->execute(["id" => $userId]); ``` The login guard identifies the account. Queries for private records must also check ownership or another permission rule. ## How Do You Track Recently Active PHP Users? Track recently active PHP users by saving their last request time on the server. A recent request suggests activity; it does not prove someone is still looking at the page. The MySQL example below assumes user_presence.user_id is a primary or unique key and last_seen_at stores UTC timestamps. Set the MySQL connection timezone to UTC with `SET time_zone = '+00:00'` before using CURRENT_TIMESTAMP, and calculate the PHP cutoff in UTC too. ```php prepare( "INSERT INTO user_presence (user_id, last_seen_at) VALUES (:user_id, CURRENT_TIMESTAMP) ON DUPLICATE KEY UPDATE last_seen_at = CURRENT_TIMESTAMP", ); $statement->execute(["user_id" => $userId]); } ``` List the active accounts by a short trailing window and let the timestamp do the work. ```php format("Y-m-d H:i:s"); $statement = $pdo->prepare( "SELECT u.id, u.email, p.last_seen_at FROM user_presence p JOIN users u ON u.id = p.user_id WHERE p.last_seen_at >= :cutoff ORDER BY p.last_seen_at DESC", ); $statement->execute(["cutoff" => $cutoff]); $onlineUsers = $statement->fetchAll(PDO::FETCH_ASSOC); ``` A single database table covers a small app comfortably. A busier chat or community app wants a shared store with expirations, refreshed through a heartbeat request. Either way, keep the claim honest and label it as presence: this account was active recently, not a guarantee that someone is staring at the page right now. ## Logout Log out by clearing the server-side session and expiring its cookie. Run this in a POST handler after starting the session and verifying a CSRF token. Deleting only the browser cookie does not invalidate a copy of the session ID. ```php time() - 3600, "path" => $params["path"], "domain" => $params["domain"], "secure" => $params["secure"], "httponly" => $params["httponly"], "samesite" => $params["samesite"] ?? "Lax", ], ); } session_destroy(); ``` Don't lean on logout as your only way to end a session. A password change, an account recovery, or a flagged login event may all need to invalidate active sessions that the user never logged out of, and you want that path built before you need it. ## Do Logged-In PHP Forms Still Need CSRF Protection? Logged-in PHP forms still need CSRF protection. Browsers can attach session cookies automatically to requests that meet the cookie’s scope and SameSite rules. Require a valid session token for state-changing forms, including logout. ```php "> ``` A valid token proves the request carried a value your server planted in the session, and it says nothing about whether the submitted data is any good. You still validate every field and you still write through prepared statements. The token closes the forgery gap, and the rest of your input handling stays exactly as important as it was. ## Does CAPTCHA Make a PHP Login Secure? CAPTCHA can reduce some automated abuse, but it does not identify a user or secure a PHP login by itself. Use it selectively alongside rate limits, password protections, session management, and accessible recovery options. Putting CAPTCHA in front of every login by default punishes legitimate users, adds accessibility friction, and still gets bypassed or outsourced to a solving service for pennies. A login flow with safe password storage, measured throttling, session rotation, and reviewable logs is a stronger first line than a challenge that mostly annoys the people you want to keep. ## How Should PHP Implement Remember Me? Implement PHP remember-me login with separate, revocable tokens rather than keeping the active session ID valid indefinitely. Use a maintained authentication library where possible. A typical design has these parts: - Store a selector alongside a hashed validator in the database.- Send the raw validator only inside the cookie.- Rotate the token every time it gets used.- Expire tokens on a deliberate schedule.- Let the user revoke a remembered device. That takes more work than increasing the session lifetime, but keeps persistent-login tokens separate from active sessions if either is compromised. ## Frequently Asked Questions ### How long should a PHP login session last? A PHP login session should expire after a defined period of inactivity and a separate maximum lifetime. Choose shorter limits for sensitive accounts. Check timestamps on the server; cookie expiry and garbage collection alone do not enforce an authentication timeout. ### Should PHP sessions use a database instead of files? File-based PHP sessions can suit one server. A database or shared store such as Redis is useful when several servers need the same sessions. Listing and revoking a user’s sessions also requires application logic; changing the storage backend alone does not add it. ### What happens when a PHP user logs in from two devices? A PHP user can have separate login sessions on two devices by default. Each device holds its own session ID. To allow only one active session, the application must track sessions and revoke earlier ones. ### Is it safe to store a user ID in a PHP session? A user ID can be stored in a server-side PHP session after authentication. Protect the session ID and storage, and check permissions separately for each private action. Do not store the user’s password in the session. ### Do you need to call session_start() on every page? Call session_start() before reading or writing PHP session data, unless the session is already active. With cookie-based sessions, start it before sending output. Requests that do not need session data can skip it. --- ## PHP Security: Passwords, Forms, Sessions, and Uploads URL: /programming/php/security/fundamentals/ Cluster: Programming > Php Secure a [PHP application](/programming/php/) by checking permissions on the server, hashing passwords, validating input, escaping output, and using prepared database queries. Keep PHP and its dependencies supported, too. Safe application code cannot fix an unpatched runtime. For the login flow, see [PHP authentication and sessions](/programming/php/security/auth-sessions/). For field checks and error messages, see [PHP form validation](/programming/php/forms/spelling-validation/). The examples below explain individual protections; they are not a complete authentication system. From what I've seen in old LAMP apps, “escape the input” blurs several different jobs. Validation checks whether a value is acceptable. Prepared statements keep values out of SQL syntax. Output escaping stops text being interpreted as markup. Permission checks decide whether the user may perform the action. ## How Should PHP Store Passwords? Store PHP passwords with `password_hash()` and check login attempts with `password_verify()`. Never store plaintext passwords or use a fast general-purpose hash such as MD5. ```php format(DateTimeInterface::ATOM); ``` Send the raw token once, then compare the submitted token's hash against the stored hash. A reset token is a credential. Give it a short lifetime, consume it once, and never store the raw token in the database. For reset flows, store the token metadata too: user id, creation time, expiry time, consumed time, IP address or coarse source context, and the reason the token was issued. That audit trail is useful when a user says they never requested a reset. It is also useful when a bot starts hammering the reset form and the app needs rate limits. Do not reveal whether an email address exists through the reset form. Return the same user-facing message for known and unknown accounts, then send a reset email only when the account exists. That feels slightly unfriendly in development, but it avoids turning the form into an account enumeration endpoint. ## How Do You Protect PHP Sessions? Protect PHP login sessions with HTTPS, secure cookie settings, session-ID rotation at login, and server-side permission checks. Set cookie options before starting the session. Enable strict mode and cookie-only IDs; custom session handlers must support ID validation for strict mode to work. ```php true, "secure" => true, "samesite" => "Lax", ]); session_start(); // After a successful login: session_regenerate_id(true); $_SESSION["user_id"] = $userId; ``` `HttpOnly` keeps ordinary client-side JavaScript away from the session cookie. `Secure` limits the cookie to HTTPS requests. `SameSite=Lax` helps with cross-site request handling, but it is defense in depth, not a complete CSRF system. The full login flow, including password verification, session rotation, logout, remember-me boundaries, and CAPTCHA tradeoffs, belongs in [PHP authentication and sessions](/programming/php/security/auth-sessions/). For the broader app, treat the session as a server-side authorization pointer. The cookie proves that the browser knows a session id, and the server still decides whether the session is active, which user it belongs to, what roles it has, and whether the specific action is allowed. That distinction matters after login because a user who can edit their own profile should not be able to edit another user's profile because the URL id changed. Put authorization checks beside the action, not only beside the navigation link that led to it. ```php "> ``` Do not use `GET` requests for actions that change data. That one rule prevents a surprising amount of damage. CSRF protection belongs on POST, PUT, PATCH, and DELETE-style actions because browsers attach cookies automatically. An attacker does not need to read the victim's session cookie if the victim's browser will send it to your site during a forged request. The token adds a second value the attacker cannot guess or obtain from another origin. `SameSite` cookies reduce common cross-site request paths, and they are worth setting. They do not replace tokens because real applications still have redirects, embedded flows, old browsers, mixed subdomains, API clients, and edge cases around top-level navigation. For normal PHP forms, a session token is still the plainest model. For JSON endpoints, send the CSRF token in a custom header and reject requests that do not include it. Also check `Origin` or `Referer` when practical, especially on high-impact actions. Headers are supporting evidence; the token remains the main application-level proof. ## How Do You Prevent XSS in PHP Output? Prevent XSS in PHP by encoding values for the place you display them. Use `htmlspecialchars()` for ordinary HTML text and quoted attribute values; JavaScript and URLs need different handling. ```php ``` If the field is supposed to contain HTML, use a real sanitizer with a strict allowlist. For most application fields, accepting plain text avoids the need to support user-written HTML. OWASP's XSS guidance separates output encoding by context because one escaping function cannot safely handle every location. HTML body text, HTML attributes, JavaScript strings, URLs, and CSS values all parse differently. PHP's `htmlspecialchars()` is the right default for ordinary HTML text and attributes when you pass the flags and encoding deliberately. ```php "> ``` Do not use the same helper inside a ` ``` For URLs, validate the scheme and host before rendering. For CSS, avoid rendering user-controlled values at all unless the value is mapped through a fixed allowlist. Encode for the exact place the value is going. ## Which Security Headers Should PHP Send? PHP can send Content Security Policy, X-Content-Type-Options, and Referrer-Policy headers to restrict browser behavior. Configure them for the scripts, assets, and services your application actually uses. Content Security Policy is the header most teams notice first because it can block inline script execution and restrict where scripts, styles, images, frames, and form posts can load from. Start with a report-only policy if the app is already live, because old templates often have inline scripts and third-party embeds hiding in places nobody remembers. ```php prepare( "SELECT id, email FROM users WHERE email = :email", ); $statement->execute(["email" => $email]); ``` PDO parameters are not a template system for arbitrary SQL; they represent data literals. If the user controls a column name, direction, or table choice, map it through a fixed allowlist before it ever reaches the query string. I've worked through enough codebases where `magic_quotes_gpc` was silently off, or had been stripped mid-deploy, to know that any approach relying on the runtime config to enforce escaping will eventually betray you. Read [PHP and MySQL with PDO](/programming/php/mysql/pdo/) if you are building database-backed forms. OWASP's SQL injection guidance still puts prepared statements first because they separate code from data. That protection is strongest when the SQL structure is fixed and values are bound through placeholders. For sortable tables, build the variable part from an allowlist: ```php "created_at", "email" => "email", "name" => "display_name", ]; $allowedDirections = [ "asc" => "ASC", "desc" => "DESC", ]; $sort = $_GET["sort"] ?? "created"; $direction = $_GET["direction"] ?? "desc"; $sortColumn = is_string($sort) ? ($allowedSorts[$sort] ?? "created_at") : "created_at"; $sortDirection = is_string($direction) ? ($allowedDirections[$direction] ?? "DESC") : "DESC"; $statement = $pdo->prepare( "SELECT id, email, display_name FROM users ORDER BY {$sortColumn} {$sortDirection} LIMIT 50", ); $statement->execute(); ``` The important part is that the user supplied a key, not SQL. If you concatenate raw `$_GET["sort"]` into `ORDER BY`, prepared statements cannot save that part of the query because identifiers and keywords are not data literals. The error path deserves the same attention. A production app should log database errors server-side and show a dull user-facing message. Stack traces, SQL strings, table names, and connection details are useful to developers and useful to attackers, which is why they belong in logs with access control. ## How Do You Handle PHP File Uploads Safely? Handle PHP uploads by limiting their size and type, generating filenames, and storing them outside executable public directories. - Store uploads outside the web root when possible.- Generate your own filenames.- Check size limits before processing.- Verify the MIME type and file content.- Never execute uploaded files as PHP. The failure mode here is severe enough to treat separately. One loose upload directory can turn a small app into a remote shell. The PHP manual's upload flow starts with `$_FILES`, but the security decision starts before PHP sees the file. Set size limits in configuration and in application code. Decide which file types the app really needs. Decide whether uploads are public, private, temporary, or derived into safer output. For images, treat the extension as a label rather than proof. Check the upload error code, enforce the size, inspect the MIME type with server-side tools, generate a new filename, and move the file with `move_uploaded_file()`. ```php 2_000_000) { throw new RuntimeException("File is too large"); } $finfo = new finfo(FILEINFO_MIME_TYPE); $mime = $finfo->file($file["tmp_name"]); $extensions = [ "image/jpeg" => "jpg", "image/png" => "png", "image/webp" => "webp", ]; if (! isset($extensions[$mime])) { throw new RuntimeException("Unsupported file type"); } $name = bin2hex(random_bytes(16)) . "." . $extensions[$mime]; $target = __DIR__ . "/../storage/uploads/" . $name; if (! move_uploaded_file($file["tmp_name"], $target)) { throw new RuntimeException("Could not store upload"); } return $name; } ``` OWASP's upload guidance also calls out the less glamorous parts: allowlist extensions, change filenames, limit file size, store outside the web root where possible, scan or process files when the risk justifies it, and serve private files through an application-controlled route. If the upload eventually becomes a thumbnail, a profile image, or a downloadable report, store the original and the public derivative as separate decisions. Never let a user upload something that the web server can execute as PHP. Also check server configuration so an upload directory cannot run scripts even if a dangerous file lands there. MIME detection alone does not prove a file is harmless. For images, also limit dimensions and consider decoding and re-encoding them before serving them. ## What Should PHP Validate on the Server? PHP should validate the type, length, range, and business rules of incoming values before using them. Browser validation can help users, but requests can bypass it. For example, an email field might need all of these: - Browser hints such as `type="email"` for user experience.- Server-side syntax validation before the write.- A unique database constraint for account identity.- Output escaping when the email appears in HTML.- Rate limits when the field is part of login or reset. The [form validation in PHP](/programming/php/forms/spelling-validation/) guide covers the form layer in more detail. The security point is that each layer has a separate job. If a rule matters to the server, enforce it on the server. ## How Do You Avoid Unsafe PHP Commands and Includes? Avoid passing user input to shell commands, `eval()`, or `include`. Use a PHP library instead of a shell command where possible. Choose template files from a fixed map rather than constructing paths from a request parameter. If an external command is unavoidable, fix the executable, validate allowed arguments, and use a library that passes arguments separately. Escaping shell characters alone does not prevent a value from being interpreted as a command-line option. ## How Should You Configure PHP for Production? PHP configuration can reduce damage, but it cannot make unsafe code safe. In production, errors should go to logs rather than the browser. The web server should enforce HTTPS on every route that handles cookies or credentials. Secrets should live outside the repository and outside any generated static output. The upload directory should have conservative permissions. Dependencies should be pinned and updated with a reason, not grabbed during a deploy because a tutorial said so. These settings are worth checking before launch: - `display_errors` is off in production.- Errors are logged somewhere the team actually reads.- Session cookies use `Secure`, `HttpOnly`, and an intentional `SameSite` value.- Upload limits match the application rule, not only the PHP default.- Public directories cannot execute uploaded scripts.- Database credentials are scoped to the app's real needs.- Backups and logs do not expose reset tokens, session ids, or plaintext secrets. That last point is easy to miss. A password reset token is uselessly "secure" if the raw token lands in an access log, application log, analytics event, or support screenshot. ## Which PHP Requests Need Rate Limits? Rate-limit PHP login attempts, password resets, registrations, uploads, and other expensive or easily abused requests. A reset endpoint should not let one visitor send thousands of emails. Rate limiting can be simple at first: count attempts by account, IP address, session, or a combination; store a short expiry; and slow or block repeated failures. The exact storage can be Redis, a database table, or a reverse proxy rule, but the important point is that the application has a memory of repeated attempts. ```php prepare( "SELECT COUNT(*) FROM rate_limit_events WHERE event_key = :event_key AND created_at > (CURRENT_TIMESTAMP - INTERVAL 10 MINUTE)", ); $statement->execute(["event_key" => $key]); return (int) $statement->fetchColumn() >= 10; } ``` This query only checks recent events. The application must also record attempts and expire old rows. A separate count and insert can race under concurrent traffic, so production limits need an atomic counter or equivalent coordination. Do not make the rate-limit key only the email address, because that lets an attacker lock out a victim by repeatedly trying their address. Do not make it only the IP address, because shared networks and proxies can make that unfair to real users. Use layered signals and keep the user-facing message boring. Abuse handling is security work even when it does not look like cryptography. A reset flow that can send 5,000 emails in a minute is a security bug, an operations bug, and a reputation bug at the same time. ## Common Pitfalls & Debugging ### A Legacy Hash Column Truncates New Password Hashes Symptom: every account fails to log in after an app switches from a hand-rolled hash to `password_hash()`, even though the migration script reported success. Cause: the password column was sized for an old MD5 or SHA1 value at 32 or 40 characters, and a bcrypt hash from `PASSWORD_DEFAULT` is 60 characters, so MySQL truncates every stored hash on insert unless strict mode rejects the write outright. Fix: widen the column to `VARCHAR(255)` before running the migration, then re-hash a test account and confirm `password_verify()` succeeds against the value actually stored in the database, not against the value the script printed. ### The Secure Cookie Flag Gets Removed to Fix Local Development Symptom: the session cookie never appears in the browser on a local server, even though the code matches the session example above exactly. Cause: the `secure` flag tells the browser to refuse the cookie over plain HTTP, and most local setups serve over HTTP, so the browser drops it silently with no PHP error to explain why. Fix: serve local development over HTTPS with a tool such as `mkcert`, or set the flag from an environment check. A secure flag deleted to make local testing convenient is easy to forget and ships straight to production. ### Headers Already Sent Blocks the Session or the Redirect Symptom: PHP reports `Cannot modify header information - headers already sent`, and the session cookie, a redirect, or a security header silently fails to apply. Cause: something sent output before `session_start()`, `setcookie()`, or `header()` ran, often a blank line before the opening PHP tag or a stray character after a closing one, and HTTP headers can only be sent before any body content. Fix: the warning names the file and line where output began, so open that exact location, remove the leading whitespace or early output, and keep header-setting calls at the top of the request. ### The Back Button Shows a Page After Logout Symptom: a user logs out, presses the browser back button, and sees the previous authenticated page again. Cause: the browser served that page from its own cache rather than making a new request, so the server-side session check never ran a second time. Destroying a session on the server does nothing to a page already sitting in the browser. Fix: send a no-store cache header on pages with account-specific data, invalidate the session at logout, and keep checking permissions on every request. Browser back-navigation behavior varies, so test it rather than treating the cache header as a logout guarantee. ```php ``` ### The Uploaded File's Reported Type Is Not Its Real Type Symptom: a file passes the upload form's type check and turns out not to be the image it claimed to be. Cause: the check read `$_FILES["file"]["type"]`, which is the MIME type the browser sent in the request, a value the person uploading the file fully controls and can set to anything regardless of the real content. Fix: detect the type from the file's actual bytes with `finfo`, the way the upload example above does, and never use the client-supplied type field for a security decision. ## Check These Protections Before Launch Before a PHP app accepts real users: 1. Passwords use `password_hash()` and `password_verify()`.2. Generated passwords and reset tokens use `random_int()` or `random_bytes()`.3. Sessions use secure cookie settings and regenerate IDs on login.4. State-changing forms use CSRF tokens.5. Form input is validated on the server before writes.6. HTML output is escaped at render time.7. SQL values go through prepared statements.8. File uploads are renamed, checked, and stored safely.9. Errors are logged server-side without showing stack traces to users.10. Authorization checks run on the server for every sensitive action.11. Upload directories cannot execute scripts.12. Reset tokens, session ids, and secrets stay out of logs.13. Rate limits protect login, reset, upload, comment, and contact routes.14. Security headers match the app's real template and asset model. ## Frequently Asked Questions ### Is HTTPS enough to secure a PHP application? HTTPS alone cannot secure a PHP application. It protects data moving between browser and server. It does nothing about injection, weak session handling, missing authorization checks, or unsafe file uploads, which are attacks against the application itself rather than the connection. ### What is the difference between authentication and authorization? Authentication establishes who the user is. Authorization decides what that user may do. A logged-in account can still be forbidden from editing another person's record, so both checks belong on every request that touches protected data. ### What is the difference between hashing and encryption? Hashing is one-way and used for passwords. For PHP passwords, use password_verify() to check the submitted password against the stored hash. Encryption is reversible with a key and used for data you must read back, such as stored tokens. Never store a password with encryption you can undo. ### Does PHP input need validation when output is escaped? PHP input still needs validation even when output is escaped. Escaping controls how text is displayed; validation checks whether a value should be accepted. Escaping cannot stop a negative order quantity, an out-of-range date, or a value the business rules forbid. ### Should you write your own encryption in PHP? Do not design your own encryption in PHP. Use maintained cryptographic libraries such as Sodium for encryption and password_hash() for password storage. Correct cryptography depends on details such as key management, initialization vectors, and constant-time comparison that are easy to get subtly wrong. ### How important is keeping the PHP version current? Keeping PHP supported ensures your runtime receives security fixes. PHP branches currently receive two years of active support followed by two years of critical security support. Check the support table for the exact end date of your branch. ## Self-Check 1. Which function produces a password hash, and which one checks a password against it? 2. Why is `VARCHAR(255)` recommended for a password column? 3. What should you call after a successful login, and what attack does it stop? 4. A CSRF token protects against what, and what does it not protect against? 5. Where does escaping belong: on the way into the database, or on the way out? 6. Why is a prepared statement safer than escaping a value into the SQL string? 7. What is wrong with trusting the uploaded file's reported type? ### Answers 1. `password_hash()` returns the hash and your own code stores it. `password_verify()` checks a password against a stored hash. Never compare hashes with `==`. 2. Future-proofing. Current bcrypt hashes are 60 characters, but the default algorithm and its length can change. A column too narrow for the stored hash truncates it, and every login then fails. 3. `session_regenerate_id(true)`. It stops session fixation, where an attacker plants a known session identifier before the victim logs in. 4. It stops another site making a request as the logged-in user. It does nothing about a script running on your own page, which is what output escaping and CSP address. 5. On the way out. Escape at the point of output, for the context you are writing into, because the same stored value is safe in one context and dangerous in another. 6. Placeholders keep bound values separate from the SQL structure. A value cannot become syntax, whatever it contains. Dynamic identifiers such as table or column names are not parameterizable and still need an allowlist. 7. The browser supplies it and an attacker controls it. Check the real content by inspection on the server, and never derive the stored filename or extension from the upload. --- ## Writing a Template System in PHP URL: /programming/php/templates/template-system/ Cluster: Programming > Php Real applications usually use Twig, Blade, or the template layer supplied by their framework. Twig and Blade auto-escape ordinary output by default, which removes a repetitive failure point from everyday template work. Building the small renderer in this tutorial teaches the mechanics of file resolution, variable scope, buffering, layouts, and escaping. It is not a recommendation to replace a maintained template engine. Either way, a template layer keeps [PHP logic](/programming/php/) from melting into the HTML until every page is a pile of `echo` statements and half-remembered includes. Picture the template boundary as a service counter. The route prepares approved data behind the counter, the renderer passes it through one controlled opening, and the view arranges the HTML. Database work, authorization, and raw request input stay behind the counter. ## How Do You Organise PHP Template Files? Keep PHP templates in a `views` directory, the renderer in `src`, and the public entry point in `public`: ```text public/ index.php src/ view.php views/ layout.php articles/ show.php ``` The controller or route prepares data, the view renders it, and the template does not query the database, read `$_POST`, or decide whether the current user is allowed to see the page. ```php "Template Systems", "body" => "A view should be boring enough to debug at 11:30 p.m.", ]; echo view("articles/show.php", [ "article" => $article, ]); ``` That is enough structure to start because the page code owns the data while the view file owns the HTML. The controller supplies the data the template needs. ## How Do You Render a PHP Template as a String? Render a PHP template as a string by opening an output buffer, including the trusted template, then collecting the output with `ob_get_clean()`. The included file inherits the variable scope at the include line. That behavior is why a small renderer can pass data into a view file. Output buffering then lets the renderer capture the view output as a string instead of sending it immediately. ```php $bufferLevel + 1) { if (!ob_end_flush()) { throw new RuntimeException("Template left a non-removable buffer open."); } } if (ob_get_level() !== $bufferLevel + 1) { throw new RuntimeException("Template closed the renderer buffer."); } return ob_get_clean(); } catch (Throwable $error) { while (ob_get_level() > $bufferLevel) { if (!ob_end_clean()) { break; } } throw $error; } } ``` Save `view()` and the `e()` helper below in `src/view.php`. Templates must be trusted PHP files, must not close the renderer’s buffer, and may open only removable buffers. This is not a sandbox. For the buffer functions, see the output-buffer functions explained in this example. First, the template name is resolved under one known `views/` directory. Do not let a request parameter choose an arbitrary file to include. That turns the renderer into a file-inclusion bug. Second, output buffering is used so the caller gets a string. Returning a string lets you wrap the output in a layout or handle a rendering error before sending HTML to the browser. Third, `extract()` is used only on data controlled by the application. PHP's own manual warns against using `extract()` on untrusted data. If the array came from `$_GET`, `$_POST`, or decoded JSON, validate it first and pass only the view variables the template needs. With this implementation, data keys named `template`, `data`, `base`, or `path` are dropped because those variables already exist in the renderer's scope. ## How Do You Escape Text in PHP Templates? Escape ordinary text in PHP templates with `htmlspecialchars()` before placing it in HTML text or quoted attributes. ```php

    ``` `htmlspecialchars()` is the ordinary escape function for text going into HTML. It turns characters such as `<`, `>`, `&`, and quotes into HTML entities. That does not make every context safe. A URL, a JavaScript string, a CSS value, and HTML body text are different contexts. For a small PHP template system, the right first rule is simple. Escape all normal text, and do not put untrusted values into JavaScript or CSS templates. If you need raw HTML, make it explicit: ```php
    ``` That variable name should make you uncomfortable enough to ask where the HTML was sanitized, and that discomfort is the point. ## How Do You Reuse a PHP Page Layout? Most pages need the same outer document: `doctype`, head, navigation, and footer. Render the page content first, then pass it into the layout. ```php $data["title"] ?? "CodeWalkers", "content" => $content, ]); } ``` The layout can then own the shell: ```php <?= e($title) ?>
    ``` Notice the difference between `$title` and `$content`. The title is text, so it is escaped. The content is already rendered HTML from a trusted template, so it is not escaped again. ## What Logic Belongs in a PHP Template? Templates are allowed to contain small display logic: a loop over comments, a condition for an empty state, or date formatting through one clear project helper. This is the kind of display logic that can stay in the view: ```php

    No comments yet.

    ``` This is where the view boundary has started to fail: ```php query("SELECT * FROM comments")->fetchAll(); ?> ``` The template is not the place to open a database connection. Put that work in the route, repository, or service layer, then pass the result into the view. Read [PHP and MySQL with PDO](/programming/php/mysql/pdo/) for the database boundary and [Beginning Object-Oriented Programming in PHP](/programming/php/oop/) for the object boundary. ## When Is a Small PHP Renderer Enough? A small PHP renderer is enough when trusted developers maintain a few pages and need only simple layouts and partials: - The template authors are trusted developers. - The app has a small number of pages. - You do not need inheritance, macros, filters, translation tooling, or sandboxing. - You can enforce escaping habits in code review. It stops being enough when the template language becomes a project inside the project. The usual sequence starts with replacing ``{`{title}`}``, then adds loops, conditions, includes, caching, filters, and custom debugging. At that point, use a maintained template engine or the framework's view layer. ## Common Pitfalls & Debugging ### The Template Path Escapes the Views Directory A crafted template name includes a file outside `views/`. The renderer joins strings without resolving and checking the final path. Resolve the base and candidate with `realpath()`, then require the candidate to start with the base plus `DIRECTORY_SEPARATOR`. ### An Exception Leaves an Output Buffer Open Later output disappears or lands in the wrong buffer after a template throws. The renderer starts buffering but cleans it only after a successful include. Record `ob_get_level()` before the include, unwind every buffer above that level on failure, and rethrow the exception. ### Trusted HTML Bypasses Escaping Stored text renders active markup or script. A value was labelled trusted without a defined sanitizer or ownership boundary. Escape ordinary text with `e()` and allow raw HTML only from a reviewed, sanitized source. ## Frequently Asked Questions ### Should you build your own PHP template engine? Build a small PHP renderer to learn file resolution, scope, buffering, layouts and escaping; use a maintained engine when the application needs a full template language. For untrusted template authors, no general-purpose PHP template layer is safe by default. Twig's sandbox needs an explicit allowlist and still does not bound CPU or memory, so treat untrusted templates as a separate hard problem. ### Can a PHP view read $_SESSION or $_POST directly? A PHP view can read $_SESSION or $_POST because superglobals are available inside included files, but passing validated data from the controller makes its dependencies clearer. A view reaching into $_POST is no longer receiving only the data the controller approved. ### Does a plain PHP renderer cache compiled templates? A plain PHP renderer includes and executes the view on each call, but OPcache can cache the compiled PHP bytecode. The renderer does not cache the finished HTML. Template engines such as Twig can also cache their generated PHP files. ### Can a PHP view render another template as a partial? A PHP view can call the view() renderer to include a partial. Because the function returns a string, one view can render a header or a list item and drop the result into its own output, which is the same mechanism the layout uses to nest a view inside itself. ### Should a PHP view query the database directly? A PHP view should receive prepared data rather than query the database itself. Letting a template query the database hides data access inside rendering, makes queries hard to test, and risks running a query for every loop iteration in a list. Fetch everything the view needs beforehand and pass it in as plain variables. ## Choose a Renderer You Can Maintain For a Laravel app, use Blade; for a Symfony app, use Twig; and for a small framework or custom app, a plain PHP renderer can be reasonable if the team understands the escape boundary. What you should not do is rebuild a full template language casually. Template engines look small from the outside because the hard parts are hidden: escaping, context rules, inheritance, caching, file loading, error messages, and developer ergonomics. Separate the data work from the display work, make escaping boring, keep file paths locked down, and stop before your "simple" template system becomes the least-tested framework in the app. For adjacent PHP boundaries, read [PHP security fundamentals](/programming/php/security/fundamentals/), [Build your own API with PHP](/programming/php/apis/custom-api/), and [Building a small PHP database app](/programming/php/mysql/dynamic-sites/). --- ## PHP Tutorials: Build Real Projects Step by Step URL: /programming/php/tutorials/ Cluster: Programming > Php Build a small PHP project or choose a task such as sending email, resizing images, or reading a file. These tutorials explain the code and link to the PHP concepts each project uses. If you are new to the language, start with the [PHP guide](/programming/php/) before choosing a project. ## Which PHP Tutorial Should You Start With? Start with the dynamic-site tutorial if you can write a PHP function and understand an HTML form. Follow this order: [Build Dynamic Sites with PHP and MySQL](/programming/php/mysql/dynamic-sites/) first, because it teaches the request-to-database loop the database projects reuse. Then [the mini chat app](/programming/php/mysql/mini-chat/) for forms and polling, and [Add Search to a PHP and MySQL App](/programming/php/mysql/search-application/) once you have data worth searching. Keep [PHP Security Fundamentals](/programming/php/security/fundamentals/) open from your first database-backed form. Shorter jobs are here too. [Sending email from PHP](/programming/php/email/sending-email/) or [creating image thumbnails](/programming/php/images/thumbnails/) covers one task rather than a whole application. Email needs a configured mail provider; thumbnails need PHP’s GD extension. ## Databases and MySQL These PHP projects connect forms and pages to a MySQL database. Use the PDO guide when you need connection and query details. - [Build Dynamic Sites with PHP and MySQL](/programming/php/mysql/dynamic-sites/): the foundational request-query-render loop. - [PHP and MySQL with PDO](/programming/php/mysql/pdo/): the modern database layer the other tutorials build on. - [Build a Mini Chat App with PHP](/programming/php/mysql/mini-chat/): forms, inserts, and a polling refresh in one small app. - [Add News and Comments with PHP and MySQL](/programming/php/mysql/news-comments/): one-to-many data in a real page. - [Add Search to a PHP and MySQL App](/programming/php/mysql/search-application/): LIKE queries, relevance, and result pages. ## Forms and Validation - [PHP Email Validation](/programming/php/forms/email-validation/): validating addresses server-side before anything is stored or sent. - [Form and Spelling Validation in PHP](/programming/php/forms/spelling-validation/): checking user input beyond the format level. ## Email, Messaging, and APIs - [Sending Email in PHP](/programming/php/email/sending-email/): text and HTML mail with attachments, done safely. - [Sending SMS with HTTP APIs](/programming/php/apis/sending-sms/): reaching phones from PHP through an HTTP gateway. - [Build a Custom API with PHP](/programming/php/apis/custom-api/): exposing your own application's data as an API. ## Images and Media - [Create Image Thumbnails with PHP](/programming/php/images/thumbnails/): resizing images with GD while keeping their proportions. - [Composite Images with PHP and GD](/programming/php/images/gd-overlapping-images/): layering and overlapping images server-side. - [Streaming Media with PHP](/programming/php/media/streaming-media/): serving audio through PHP rather than direct file links. ## Files, Templates, and XML - [Working with Text Files in PHP](/programming/php/files/text-files/): reading, writing, and appending safely. - [Writing a Template System in PHP](/programming/php/templates/template-system/): separating markup from logic by hand, which is what every template engine automates. - [Processing XML with PHP](/programming/php/xml/processing-xml/): parsing feeds and structured documents. - [Recursion in PHP](/programming/php/functions/recursion/): the technique behind directory walkers and tree menus. ## Networking and the Browser - [Using Sockets in PHP](/programming/php/networking/sockets/): talking to other servers below the HTTP layer. - [Smart Auto Refresh with PHP](/programming/php/browser/smart-auto-refresh/): polling for new records and deciding when to update or reload the page. ## Security References Use these references when your PHP project accepts input or handles accounts. - [PHP Security Fundamentals](/programming/php/security/fundamentals/): the attacks every PHP application faces and the habits that prevent them. - [PHP Authentication and Sessions](/programming/php/security/auth-sessions/): a reference for login systems and session handling. ## Beyond the Tutorials The [PHP guide](/programming/php/) is the language's home page, and the Guides group in the sidebar covers the concept side: [control flow](/programming/php/control-flow/), [object-oriented PHP](/programming/php/oop/), [configuration](/programming/php/configuration/), the [LAMP stack](/programming/php/lamp-stack/), and [the framework landscape](/programming/php/frameworks/). When a tutorial's database side raises questions, the [SQL section](/programming/sql/) explains the database concepts. --- ## PHP XML Guide: SimpleXML, XMLReader, and XMLWriter URL: /programming/php/xml/processing-xml/ Cluster: Programming > Php XML has fallen out of fashion for new web APIs, but it has not disappeared from real [PHP work](/programming/php/). RSS feeds, old content exports, banking files, shipping integrations, government services, and enterprise systems still hand you XML and expect you to parse it without drama. The answer is narrow. Use XML when the contract is already XML, and use JSON when you control the new contract. The job in either case is the same, which is to read a feed, pull values out of a document, and turn someone else's structured data into application data. A parser makes the document easier to read, and it does not make the data trustworthy. ## Guide Path Read this after [working with text files in PHP](/programming/php/files/text-files/) if the file is structured XML instead of plain text. When you control the new integration contract, jump ahead to the [custom API capstone](/programming/php/apis/custom-api/) instead of inventing new XML. ## How Do You Read XML with PHP SimpleXML? Read a small XML document with `simplexml_load_string()`, check for failure, then access its elements as properties. PHP must have the SimpleXML extension enabled. ```php Practical PHP CodeWalkers XML; $catalog = simplexml_load_string($xml); if ($catalog === false) { throw new RuntimeException("Could not parse XML."); } foreach ($catalog->book as $book) { printf( "%s by %s\n", (string) $book->title, (string) $book->author, ); } ``` Those explicit casts matter at the application boundary. A `SimpleXMLElement` is not the same thing as a string, even when it prints like one. Cast values at the boundary where the data enters your own model. ## How Do You Handle PHP XML Parse Errors? Handle XML parse failures by checking for `false` and collecting libxml errors. Common causes include a truncated upload, a bad character, a provider response wrapped in an HTML error page, or a feed that changed its namespace. Treat those failures as input errors. ```php message); } libxml_clear_errors(); libxml_use_internal_errors($previousErrorMode); throw new RuntimeException( "Invalid XML: " . implode("; ", $messages), ); } libxml_clear_errors(); libxml_use_internal_errors($previousErrorMode); ``` Check with `=== false`, not a loose truthiness check. An empty but valid XML document can look falsey in PHP. That is the kind of detail that makes old XML code feel haunted when the real problem is just a loose comparison. ## How Do You Read Large XML Files in PHP? Read large XML files in PHP with XMLReader, which moves through the document one node at a time. SimpleXML loads the whole document into memory, so reserve it for smaller files. ```php read()) { if ($reader->nodeType !== XMLReader::ELEMENT || $reader->name !== "book") { continue; } $book = simplexml_load_string($reader->readOuterXml()); if ($book === false) { continue; } importBook([ "id" => (string) $book["id"], "title" => (string) $book->title, "author" => (string) $book->author, ]); } $reader->close(); ``` The example assumes your application defines `importBook()`. XMLReader avoids loading the whole document, but `readOuterXml()` still loads each matching record into a string. A single huge record can consume substantial memory; set file and record limits and check parse errors rather than treating every early stop as success. If the next step writes rows into SQL, batch the writes and use prepared statements. XML carries the data, but you still need to choose suitable database tables, column types and relationships. The [schema design guide](/programming/sql/schema-design/) covers that side of the work. ## How Do You Read XML Namespaces with SimpleXML? Read namespaced elements with SimpleXML’s `children()` method and the namespace URI. If your code ignores them, it may work against one sample and fail against the real feed. ```php PHP GuideMark'; $feed = simplexml_load_string($rssXml); if ($feed === false) { throw new RuntimeException("Invalid RSS XML."); } $namespaces = $feed->getNamespaces(true); foreach ($feed->channel->item as $item) { $dc = $item->children($namespaces["dc"] ?? ""); echo htmlspecialchars((string) $item->title, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); echo htmlspecialchars((string) $dc->creator, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); } ``` If you remember the old "Using Modules in Your RSS Feed" tutorial, this is its modern shape, because RSS modules are a namespace problem before they are a PHP trick. Do not strip namespaces to make the code shorter. That usually means throwing away the part of the contract that made the feed useful. ## How Do You Generate a Sitemap with PHP? Generate a PHP sitemap with `XMLWriter`, using your application’s canonical URLs and each page’s real modification date. Do this at publish time, build time, or on a cacheable admin job. Do not rebuild the sitemap by scanning live tables on every public request unless the site is tiny and you have measured it. `XMLWriter` keeps the output well formed without hand-concatenating tags: ```php "https://example.com/programming/php/", "lastmod" => new DateTimeImmutable("2026-06-06"), ], [ "loc" => "https://example.com/programming/php/mysql/pdo/", "lastmod" => new DateTimeImmutable("2026-06-06"), ], ]; $writer = new XMLWriter(); $writer->openMemory(); $writer->startDocument("1.0", "UTF-8"); $writer->startElement("urlset"); $writer->writeAttribute("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9"); foreach ($routes as $route) { $writer->startElement("url"); $writer->writeElement("loc", $route["loc"]); $writer->writeElement("lastmod", $route["lastmod"]->format("Y-m-d")); $writer->endElement(); } $writer->endElement(); $writer->endDocument(); $sitemapXml = $writer->outputMemory(); ``` The useful rules are small: include absolute canonical URLs, keep them on the same host, escape values through the XML writer, and set `lastmod` to the page's real content modification date rather than the time the sitemap file happened to regenerate. For larger sites, split files before the protocol limits and use a sitemap index. If your framework or static-site builder already emits a sitemap from routes and frontmatter, use that. Custom PHP generation is for inherited systems, internal tools, and migrations where PHP still owns the URL inventory. ## How Do You Parse Untrusted XML Safely? Parse untrusted XML with external entities and external DTD loading disabled, and limit document size and nesting. These controls reduce the risk of file disclosure and excessive resource use. In current PHP, the libxml constants matter. `LIBXML_NONET` disables network access when loading documents. PHP 8.4 also exposes `LIBXML_NO_XXE` when built with a new enough libxml version. The older `libxml_disable_entity_loader()` path is listed as deprecated in the PHP manual, so do not copy old snippets that begin there. For ordinary untrusted input, keep the load step explicit: ```php Python A Python comprehension builds a list, dictionary, or set from an iterable in one expression. Use one to transform values or keep only the items you need. If loops are still new to you, start with the [Python learning guide](/programming/python/). ```python squares = [n * n for n in range(6)] print(squares) ``` ```text # Program output: # [0, 1, 4, 9, 16, 25] ``` ## How Do Python Comprehensions Work? A Python comprehension evaluates an expression for each item in an iterable, optionally skipping items with an `if` filter. The squares example above does the same work as this loop: ```python squares = [] for n in range(6): squares.append(n * n) ``` ### The List Form The list form is written in square brackets and has three parts, read left to right: ```text # [ expression for item in iterable if condition ] # what to keep where it comes from which ones survive ``` The expression is evaluated once for each item that passes any filters, and its result goes into the new list. The for clause names the loop variable and the iterable it walks. The if clause is optional and decides which items reach the expression at all. ```python names = [" Ada ", "bo", " Cleo"] cleaned = [name.strip().title() for name in names] print(cleaned) ``` ```text # Program output: # ['Ada', 'Bo', 'Cleo'] ``` The source can be any iterable, not only a [list](/programming/python/lists/). Strings, ranges, files, and dictionary views all work, and the result is always a new list rather than a change to the source. ### The Dict Form The dict form swaps the square brackets for braces and the single expression for a key and value separated by a colon: ```text # { key_expression: value_expression for item in iterable if condition } ``` ```python words = ["apple", "fig", "banana"] lengths = {word: len(word) for word in words} print(lengths) ``` ```text # Program output: # {'apple': 5, 'fig': 3, 'banana': 6} ``` Feeding it `.items()` is how you transform a [dictionary](/programming/python/dictionaries/) you already have. Unpacking the pair into two names keeps the body readable: ```python prices = {"apple": 1.5, "fig": 3.0} doubled = {name: price * 2 for name, price in prices.items()} inverted = {price: name for name, price in prices.items()} print(doubled) print(inverted) ``` ```text # Program output: # {'apple': 3.0, 'fig': 6.0} # {1.5: 'apple', 3.0: 'fig'} ``` If you are unsure which result type you need, compare [Python lists, dictionaries, and sets](/programming/python/lists/lists-vs-dictionaries-vs-sets/). A lossless inversion requires the values to be hashable and distinct. Repeated values still run, and they collapse, which is the collision pitfall below. ## How Do You Filter a Python Comprehension? A Python comprehension filters items with an `if` condition after the `for` clause. Items that fail it never reach the expression and never appear in the result: ```python scores = [85, 42, 91, 67, 95] passing = [score for score in scores if score >= 70] print(passing) ``` ```text # Program output: # [85, 91, 95] ``` The same clause works in the dict form, and it can test either half of the pair: ```python scores = {"Ada": 91, "Bo": 67, "Cleo": 85} top = {name: score for name, score in scores.items() if score >= 85} print(top) ``` ```text # Program output: # {'Ada': 91, 'Cleo': 85} ``` ## Where Does if-else Go in a Python Comprehension? In a Python comprehension, `if-else` goes before the `for` clause. It chooses a value for each item rather than removing items: ```python scores = [85, 42, 91] labels = ["pass" if score >= 70 else "fail" for score in scores] print(labels) ``` ```text # Program output: # ['pass', 'fail', 'pass'] ``` The position tells you which one you are reading. A bare `if` at the end drops items, and an `if-else` at the front keeps every item and changes what is stored. Both can appear in one comprehension, though that is usually the point to switch to a loop. ## How Do Nested Python Comprehensions Work? Nested Python comprehensions follow the same order as nested loops. With two `for` clauses, the outer loop comes first: ```python matrix = [[1, 2, 3], [4, 5, 6]] flat = [value for row in matrix for value in row] print(flat) ``` ```text # Program output: # [1, 2, 3, 4, 5, 6] ``` Building a nested structure is the other direction, and there the inner comprehension sits inside the expression: ```python grid = [[row * 3 + col for col in range(3)] for row in range(2)] print(grid) ``` ```text # Program output: # [[0, 1, 2], [3, 4, 5]] ``` In the matrix example, the two `for` clauses produce one flat list. In the grid example, the inner comprehension builds a separate list for each row. If you need to trace several nested loops to understand the result, use a plain loop or a named helper function. ## When Should You Use a Loop Instead of a Comprehension? Use a regular Python loop instead of a comprehension when you need several steps, per-item error handling, or actions such as writing files. A comprehension works best when the result is easy to read in one pass. - Two collections at once. One comprehension produces one result, so splitting items into passes and failures needs either two comprehensions over the same data or one loop. - Branching logic. A chain of conditions choosing between several values is usually easier to read in an `if-elif-else` block than in nested conditional expressions. - Error handling. The syntax has no room for `try`, so catching an error for each item needs a loop or a helper function. - Side effects. Printing, writing files, and calling an API are not what a comprehension is for, and using one that way builds a list of unused return values, often `None`. - Length. Breaking a comprehension across lines is fine. Switch to a loop when extra formatting still leaves the logic hard to follow. The working rule is that a reader should get the result from one pass over the line. If they have to trace it, write the loop. ## Pitfalls and Debugging ### A Comprehension Used for Its Side Effect Writing `[print(x) for x in items]` prints, and it also builds and discards a list of `None` values. Nothing raises, so the cost is invisible until the iterable is large: ```python items = ["a", "b"] wasted = [print(item) for item in items] print(wasted) ``` ```text # Program output: # a # b # [None, None] ``` The rule is that a comprehension is for producing a value. When you are not keeping the result, write `for item in items:` instead. ### The Filter and the Value Swapped Putting a bare `if` where an `if-else` belongs is a syntax error, which is the friendly version of this mistake: ```text # SyntaxError: expected 'else' after 'if' expression # labels = [ "pass" if score >= 70 for score in scores ] ``` Putting an `if-else` after the `for` clause is also invalid syntax. To keep only passing scores, use `[score for score in scores if score >= 70]`. To label every score, put `"pass" if score >= 70 else "fail"` before the `for` clause. ### Keys That Collide A dict comprehension whose key expression repeats keeps the last pair silently, because that is what assigning to an existing key does: ```python people = [("admin", "Ada"), ("user", "Bo"), ("admin", "Cleo")] by_role = {role: name for role, name in people} print(by_role) ``` ```text # Program output: # {'admin': 'Cleo', 'user': 'Bo'} ``` Ada is gone and nothing said so. When keys can repeat, the data wanted a grouping loop that appends to a list per key, not a comprehension that stores one value per key. ## Frequently Asked Questions ### Are Python comprehensions faster than loops? Python list comprehensions can be faster than equivalent loops that repeatedly call append(), but the difference depends on the Python version and the work each item requires. Choose the clearer form first and measure it if performance becomes a problem. ### Can a Python comprehension use try and except? A Python comprehension cannot contain try and except statements. Handle errors in a regular loop or in a helper function called by the comprehension. ### Does a Python comprehension overwrite an outer loop variable? A Python 3 comprehension does not overwrite an outer variable with its loop variable. The loop variable belongs to the comprehension, so an existing variable with the same name keeps its value. ### How does a Python set comprehension work? A Python set comprehension uses braces around an expression and a for clause, such as {word.lower() for word in words}. It keeps only unique results, and every result must be hashable. ### How do you write a Python comprehension over two lists? Use zip() in a Python comprehension to pair items from two lists by position. By default, zip() stops at the shorter list, so check the lengths if every item must be paired. Use two for clauses instead when you want every combination of items. ### What is a Python generator expression? A Python generator expression produces values on demand instead of building a whole collection. It uses parentheses, as in (n * n for n in range(6)), and works well when a function such as sum() consumes the values once. --- ## Python Dictionaries: Keys, Values, Updates, Loops, and JSON URL: /programming/python/dictionaries/ Cluster: Programming > Python A Python dictionary stores values under unique keys, so you can look up a name, setting, or count without remembering its position. Use one for records, configuration, and data you need to find by a label. Think of a mailroom wall with a name on each compartment. You read the label instead of counting slots. If you are still learning loops and variables, the [Python learning guide](/programming/python/) covers those first. ## What Is a Python Dictionary? A Python dictionary is a collection of key and value pairs. The key is the label, the value is whatever sits behind it, and each key appears at most once. Assigning to a key that already exists replaces its value instead of adding a second entry. ```python user = {"name": "Ada", "email": "ada@example.com", "age": 36} print(user["name"]) print(len(user)) ``` ```text # Program output: # Ada # 3 ``` Values can be anything at all, including lists, other dictionaries, and your own objects. Keys are restricted to hashable objects, which in practice means strings, numbers, booleans, and tuples of those. Lists and dictionaries cannot be keys because they can change after they are made. Dictionaries also keep insertion order, which the language has guaranteed since Python 3.7. That is the order keys were first added, not alphabetical order, and re-assigning an existing key leaves it where it was. ## How Do You Create a Python Dictionary? Create a Python dictionary with key-value pairs inside braces, or pass pairs to `dict()`. The constructor also accepts keyword arguments when the keys are valid Python names: ```python literal = {"host": "localhost", "port": 5432} keyword = dict(host="localhost", port=5432) pairs = dict([("host", "localhost"), ("port", 5432)]) empty = {} print(literal == keyword == pairs) print(type(empty)) ``` ```text # Program output: # True # ``` Note that `{}` is an empty dictionary, not an empty set. That is the one piece of Python punctuation that surprises people coming from other languages, and `set()` is how you spell the empty set. Two more routes come up constantly. Pairing two sequences with `zip` builds a record from headers and a row, and `dict.fromkeys` builds a dictionary whose keys all start on the same value: ```python headers = ["name", "age", "city"] row = ["Ada", "36", "London"] print(dict(zip(headers, row))) print(dict.fromkeys(["read", "write"], False)) ``` ```text # Program output: # {'name': 'Ada', 'age': '36', 'city': 'London'} # {'read': False, 'write': False} ``` Give `fromkeys` a mutable default such as a list and every key shares one object, which is the same trap the repeated-empty-list shortcut sets on [lists](/programming/python/lists/). A [dict comprehension](/programming/python/comprehensions/) is the safe way to give each key its own. ## How Do You Read Values From a Python Dictionary? Read a Python dictionary value with `dictionary[key]`. Square brackets raise `KeyError` if the key is missing. The `get` method asks the same question and hands back `None`, or a default you supply, instead of raising: ```python user = {"name": "Ada", "email": "ada@example.com"} print(user.get("name")) print(user.get("phone")) print(user.get("phone", "Not provided")) ``` ```text # Program output: # Ada # None # Not provided ``` ### When a Missing Key Is a Bug The choice between the two is not about safety, it is about whose fault a missing key would be. Use brackets when the key must exist and its absence means your program is already wrong, because a loud `KeyError` at the point of the mistake is worth more than a quiet `None` three functions later. Use `get` when the data comes from outside your control, such as a form field a user may leave blank or an API response with optional fields. There the absence is expected, and a default is a real answer rather than a papered-over failure. ### Missing Keys and None Values Without a distinct default, `get()` returns `None` for both a missing key and a key storing `None`. Use `in` to check whether the key exists, or supply a unique default value that cannot be confused with stored data: ```python user = {"phone": None} print(user.get("phone", "missing")) print("phone" in user) print("fax" in user) ``` ```text # Program output: # None # True # False ``` The first line looks wrong until you read it carefully. The default never applies, because the key is present, so `get` returns the stored `None`. Reach for `in` whenever "not set" and "set to nothing" mean different things in your data. ## How Do You Change a Python Dictionary? Change a Python dictionary by assigning a value to a key. A new key adds an entry; an existing key replaces its value. Use `pop()` or `del` to remove an entry: ```python user = {"name": "Ada"} user["email"] = "ada@example.com" user["name"] = "Ada Lovelace" print(user) ``` ```text # Program output: # {'name': 'Ada Lovelace', 'email': 'ada@example.com'} ``` Removal comes in three forms, matching whether you want the value back and whether you know the key: ```python user = {"name": "Ada", "age": 36, "city": "London", "role": "admin"} age = user.pop("age") last = user.popitem() del user["name"] print(age, last) print(user) ``` ```text # Program output: # 36 ('role', 'admin') # {'city': 'London'} ``` The `pop` method removes by key and returns the value, raising `KeyError` unless you pass a default as its second argument. The `popitem` method removes the most recently inserted pair and returns it as a tuple. The `del` statement removes without returning, and `clear()` empties the dictionary. ## How Do You Merge Two Python Dictionaries? Merge Python dictionaries with `left | right` to create a new dictionary, or `left.update(right)` to change the left dictionary. When both contain the same key, the right dictionary supplies its value. The `|` operator requires Python 3.9 or later: ```python defaults = {"host": "localhost", "port": 5432, "debug": False} overrides = {"debug": True} merged = defaults | overrides unpacked = {**defaults, **overrides} copy_of_defaults = dict(defaults) copy_of_defaults.update(overrides) print(merged) print(defaults) ``` ```text # Program output: # {'host': 'localhost', 'port': 5432, 'debug': True} # {'host': 'localhost', 'port': 5432, 'debug': False} ``` The `|` operator and the `**` unpacking both build a new dictionary and leave both inputs alone. The `update` method writes into the receiver, which is why the example copies first. In every form the right-hand side wins on any key both sides carry. That last rule is the whole reason the defaults-then-overrides pattern reads so well. Put the settings you shipped on the left and the settings the user chose on the right, and the merge states the precedence in one line. ## How Do You Loop Through a Python Dictionary? Loop through a Python dictionary with `for key in dictionary` to read its keys, or use `dictionary.items()` to read each key and value together: ```python user = {"name": "Ada", "age": 36} for key in user: print(key) for key, value in user.items(): print(key, "=", value) ``` ```text # Program output: # name # age # name = Ada # age = 36 ``` The three view methods, `keys()`, `values()`, and `items()`, return live views rather than snapshots. Change the dictionary and every view already handed out reflects the change: ```python user = {"name": "Ada", "age": 36} labels = user.keys() user["city"] = "London" print(labels) ``` ```text # Program output: # dict_keys(['name', 'age', 'city']) ``` That is useful when you want the view to track the data, and surprising when you expected a frozen copy. Wrap the view in `list()` when you need one that stops moving. ## Counting and Grouping Dictionaries are useful for counting and grouping. Counting stores how often each item appears. Grouping stores a list of items under each category. ```python items = ["apple", "banana", "apple", "cherry", "banana", "apple"] counts = {} for item in items: counts[item] = counts.get(item, 0) + 1 print(counts) ``` ```text # Program output: # {'apple': 3, 'banana': 2, 'cherry': 1} ``` The `get` call with a default of `0` is what removes the special case for the first sighting. Without it the loop needs a membership test before every increment. ### Setdefault and Its Sharp Edge Grouping needs a fresh list the first time a category appears. The `setdefault` method stores the default when the key is absent and returns whatever is now there, so the two steps collapse into one: ```python people = [("admin", "Ada"), ("user", "Bo"), ("admin", "Cleo")] by_role = {} for role, name in people: by_role.setdefault(role, []).append(name) print(by_role) ``` ```text # Program output: # {'admin': ['Ada', 'Cleo'], 'user': ['Bo']} ``` The sharp edge is that the default argument is evaluated before `setdefault` runs, every single time. Writing `cache.setdefault(key, fetch(key))` therefore calls `fetch` on every pass, including the ones that were meant to be cache hits. When the fallback is expensive, spell out the membership test instead so the expensive call only happens on a genuine miss. ### Defaultdict for the Whole Loop When missing keys are the expected case rather than the exception, `collections.defaultdict` moves the bookkeeping into the type. You declare a factory once and then write the loop as if every key already existed: ```python from collections import defaultdict people = [("admin", "Ada"), ("user", "Bo"), ("admin", "Cleo")] by_role = defaultdict(list) for role, name in people: by_role[role].append(name) print(dict(by_role)) ``` ```text # Program output: # {'admin': ['Ada', 'Cleo'], 'user': ['Bo']} ``` The behavior to remember is that reading a missing key creates it. After `by_role["guest"]` the guest key exists with an empty list, even though you only looked. Use `get` when you want to inspect without writing. ## How Do You Sort a Python Dictionary? Sort a Python dictionary by passing sorted key-value pairs to `dict()`. Dictionaries have no in-place `sort()` method; the new dictionary keeps the order of the pairs you supply: ```python prices = {"cherry": 2.0, "apple": 1.5, "banana": 0.75} by_key = dict(sorted(prices.items())) by_value = dict(sorted(prices.items(), key=lambda pair: pair[1])) print(by_key) print(by_value) ``` ```text # Program output: # {'apple': 1.5, 'banana': 0.75, 'cherry': 2.0} # {'banana': 0.75, 'apple': 1.5, 'cherry': 2.0} ``` The `key` function says which half of each pair to compare, so `pair[1]` sorts on values. Add `reverse=True` for descending order. When only the top few matter, `heapq.nlargest` avoids sorting the whole thing. ## Nested Dictionaries and JSON Real data nests. An API response holds a user, which holds an address, which holds a ZIP code, and each level is another dictionary. Chained `get` calls read a deep path without raising on any missing key: ```python response = {"data": {"user": {"profile": {"bio": "Engineer"}}}} bio = response.get("data", {}).get("user", {}).get("profile", {}).get("bio", "No bio") missing = response.get("data", {}).get("account", {}).get("plan", "free") print(bio) print(missing) ``` ```text # Program output: # Engineer # free ``` Each `{}` default keeps the chain going when a key is absent. It does not guard a key that is present with the value `None`: `{"data": None}` returns `None`, and the next `get` raises `AttributeError`. Validate levels that can be null before chaining. Past three levels the line stops being readable either way, and naming the intermediate shape in its own variable is usually the better answer. The `json` module is the boundary between dictionaries and text. It maps a dictionary to a JSON object and back, with a few conversions worth knowing: ```python import json record = {"name": "Ada", "active": True, "score": None, 2026: "year"} text = json.dumps(record) back = json.loads(text) print(text) print(list(back.keys())) ``` ```text # Program output: # {"name": "Ada", "active": true, "score": null, "2026": "year"} # ['name', 'active', 'score', '2026'] ``` Three conversions happen there. Python's `True` becomes `true`, `None` becomes `null`, and the integer key becomes a string because JSON objects only allow string keys. That last one means a round trip does not always give you back the dictionary you started with. Types with no JSON equivalent, such as `datetime` and `set`, raise `TypeError` on `json.dumps`. Convert them yourself before encoding, usually to an ISO date string and a list. ## A Worked Example: Summarizing Orders A list of order records has to become a per-customer total and a list of what each one bought, which is counting and grouping in the same pass. ```python orders = [ {"customer": "Ada", "item": "keyboard", "total": 80.0}, {"customer": "Bo", "item": "monitor", "total": 210.0}, {"customer": "Ada", "item": "mouse", "total": 25.0}, ] spend = {} bought = {} for order in orders: who = order["customer"] spend[who] = spend.get(who, 0) + order["total"] bought.setdefault(who, []).append(order["item"]) print(spend) print(bought) ``` ```text # Program output: # {'Ada': 105.0, 'Bo': 210.0} # {'Ada': ['keyboard', 'mouse'], 'Bo': ['monitor']} ``` Note the two access styles doing different jobs in the same loop. Reading `order["customer"]` uses brackets because a record without a customer is broken data and should fail loudly. Reading `spend.get(who, 0)` uses a default because a first-time customer is entirely normal. Ranking the result is the sort pattern from earlier, applied to the summary rather than to the raw orders: ```python spend = {"Ada": 105.0, "Bo": 210.0} ranked = dict(sorted(spend.items(), key=lambda pair: pair[1], reverse=True)) print(ranked) ``` ```text # Program output: # {'Bo': 210.0, 'Ada': 105.0} ``` ## Pitfalls and Debugging ### KeyError on Data You Do Not Control The error `KeyError: 'phone'` means the dictionary has no `phone` key. Check for a misspelled key or a field missing from the input before choosing a fallback. Fix it at the boundary rather than at the crash site. Decide once, where the data enters your program, which fields are required and which have defaults, and apply `get` or a validation step there. ### Unhashable Key Types Using a list as a key raises `TypeError: unhashable type: 'list'`. A key has to be stable, because the dictionary files it by its hash, and a list can change after filing: ```python prices = {} prices[("London", "SW1")] = 42 print(prices) ``` ```text # Program output: # {('London', 'SW1'): 42} ``` A tuple is the fix when you want a compound key, as long as everything inside it is hashable too. A tuple containing a list is not hashable and raises the same error. ### Changing Keys While Looping Adding or deleting keys while iterating is invalid and may raise `RuntimeError: dictionary changed size during iteration` or skip entries. Changing the values behind existing keys is allowed, because the set of keys is what the loop is walking. ```python counts = {"apple": 3, "banana": 2} for key in counts: counts[key] = counts[key] * 2 print(counts) for key in list(counts): if counts[key] > 5: del counts[key] print(counts) ``` ```text # Program output: # {'apple': 6, 'banana': 4} # {'banana': 4} ``` Wrapping the dictionary in `list()` takes a snapshot of the keys, so the loop walks a fixed sequence while the dictionary underneath it changes. ### A Shallow Copy That Shares Its Insides The `copy()` method and `dict(original)` both copy the outer dictionary only. Nested dictionaries and lists are still shared, so a change through one copy shows up in the other: ```python import copy settings = {"window": {"width": 800}} shallow = settings.copy() deep = copy.deepcopy(settings) shallow["window"]["width"] = 1024 print(settings["window"]["width"]) print(deep["window"]["width"]) ``` ```text # Program output: # 1024 # 800 ``` Use `copy.deepcopy` when the nested objects need to be independent, and remember it walks the whole structure, so it is not free on large data. ## When Should You Use Something Other Than a Dictionary? Use a class or dataclass for records with fixed fields, a set when you only need unique values, or a list when positions matter more than names. A dictionary fits data you want to look up by key. - When the keys are always the same handful of names, a class or a `dataclass` gives you attribute access, a readable repr, and an error when you try to read a field that does not exist. - When you only ever ask whether something is present, a set stores the labels without the empty pigeonholes behind them. - When order and position are the point, a [list](/programming/python/lists/) says so, and a dictionary keyed by `0`, `1`, and `2` is a list wearing a disguise. Our [comparison of lists, dictionaries, and sets](/programming/python/lists/lists-vs-dictionaries-vs-sets/) shows when each collection fits. ## Frequently Asked Questions ### Are Python dictionaries ordered? Python dictionaries preserve insertion order, a language guarantee since Python 3.7. Keys appear in the order they were added, not sorted order. Assigning a new value to an existing key leaves its position unchanged. ### What can you use as a Python dictionary key? A Python dictionary key must be hashable. Strings, numbers, and tuples containing only hashable objects work. Lists, dictionaries, and sets are unhashable, so using one as a key raises TypeError. ### Can two Python dictionary keys hold the same value? Two or more Python dictionary keys can hold the same value. Only the keys must be unique. Assigning to an existing key replaces its value instead of adding a second entry. ### How do you check whether a Python dictionary key exists? Use key in dictionary to check whether a Python dictionary contains a key. Calling get() without a distinct default cannot tell a missing key apart from a key storing None. If you also need the value, pass a unique sentinel object as the default. ### How do Python dict.pop() and dict.popitem() differ? Python dict.pop() removes a specified key and returns its value. dict.popitem() removes the last inserted entry and returns a key-value tuple. pop() accepts a default for a missing key; popitem() raises KeyError when the dictionary is empty. ### Are Python dictionaries thread-safe? A Python dictionary does not make a sequence of reads and writes thread-safe. Another thread can change the data between those steps. Protect shared multi-step updates with a lock rather than assuming interpreter protections make the whole operation atomic. ### How do you turn two Python lists into a dictionary? Use dict(zip(keys, values)) to turn two Python lists into a dictionary, pairing items by position. By default, zip() stops at the shorter list, so check the lengths when every item must be paired. Repeated keys keep the last paired value. ### Why does JSON turn Python dictionary integer keys into strings? JSON objects only allow string keys, so Python's json.dumps() converts integer dictionary keys to strings. Loading that JSON keeps the string keys. A dictionary with integer keys therefore changes when you serialize it to JSON and load it again. ## Self-Check 1. What does `{}` create, and how do you spell an empty set? 2. Given `user = {"phone": None}`, what does `user.get("phone", "missing")` return, and why? 3. Which of `update` and `|` changes the dictionary on the left? 4. Why does `cache.setdefault(key, fetch(key))` call `fetch` on every pass? 5. What happens to a `defaultdict(list)` when you merely read a key that is not there? 6. Why can a tuple be a dictionary key when a list cannot? 7. After `json.loads(json.dumps({1: "a"}))`, what is the key, and why? ### Answers 1. An empty dictionary. The empty set has no literal spelling, so it is written `set()`. 2. `None`. The key is present, so the default never applies and the stored value comes back. Use `"phone" in user` to test presence. 3. `update`. It writes into the receiver, while `|` builds a new dictionary and leaves both inputs alone. 4. Because arguments are evaluated before the call. Python computes `fetch(key)` first, then passes the result in, so a cache hit still pays for the fetch. 5. The key is created with an empty list. Reading a missing key runs the factory and stores the result, so use `get` when you want to look without writing. 6. A tuple is hashable only when every element is hashable. Lists do not support hashing, so neither a list nor a tuple containing one can be a dictionary key. 7. The string `"1"`. JSON objects only allow string keys, so the integer key is converted on the way out and stays a string on the way back. ## Where to Go Next Practice choosing between brackets for required keys and `get()` for optional values. Then try the counting and grouping examples with your own records. --- ## Python List append(): Add One Item to the End of a List URL: /programming/python/lists/append/ Cluster: Programming > Python The `append()` method adds one item to the end of a [Python list](/programming/python/lists/). It's the workhorse of the list methods, and it changes the list you call it on, so the new item is sitting right there the moment the line runs: ```python cart = ["milk", "eggs"] cart.append("bread") print(cart) print(len(cart)) ``` ```text # Program output: # ['milk', 'eggs', 'bread'] # 3 ``` `append()` returns `None`, not the updated list. Call it on its own line so you keep the list you just changed. ## Syntax and Basic Usage ```python items.append(x) # adds x to the end and returns None ``` - x (required, any type): the item to add. It goes on as one item, whatever it is. - Returns: `None`. The list itself is what changes. - Raises: `TypeError` when you pass no argument or more than one. A list doesn't care what type you hand it, so you can mix strings, numbers and even `None` in the same list: ```python todo = [] todo.append("write tests") todo.append(42) todo.append(None) print(todo) ``` ```text # Program output: # ['write tests', 42, None] ``` Starting from an empty list and appending as you go is the most common way to build a list in Python, so get comfortable with it early. It works like the deli line at a Wawa on a Saturday morning: every new order joins the end, and nobody already waiting gets bumped. ## What Happens When You Append a List in Python? Python `append()` adds a list as one nested item. It does not unpack the list's contents. When you want the elements added one by one, that's the job `extend()` was built for: ```python first = [1, 2] second = [1, 2] first.append([3, 4]) second.extend([3, 4]) print(first, len(first)) print(second, len(second)) ``` ```text # Program output: # [1, 2, [3, 4]] 3 # [1, 2, 3, 4] 4 ``` The two lengths in that output tell the story. The first list grew by one, because the nested `[3, 4]` counts as a single item, while the second grew by two. Strings trip people the other way, because `extend()` loops over whatever you give it, and looping over a string gives you its characters: ```python letters = ["a", "b"] letters.append("cd") print(letters) letters.extend("ef") print(letters) ``` ```text # Program output: # ['a', 'b', 'cd'] # ['a', 'b', 'cd', 'e', 'f'] ``` So when you're adding a single string, use `append()`, because `extend()` would split it into characters. When you've got a list of new items, `extend()` is the one you want, because it adds each of them separately. If the loops below are new to you, start with the [Python learning guide](/programming/python/). ## Practical Examples ### Keeping the Items That Pass a Test The classic pattern is an empty list before a loop and an `append()` inside an `if`. Here it keeps only the scores that fall between 0 and 100: ```python scores = [88, -1, 72, 105, 64] valid = [] for score in scores: if 0 <= score <= 100: valid.append(score) print(valid) ``` ```text # Program output: # [88, 72, 64] ``` A list comprehension can do this filter in one line. For a single test, I'd choose that shorter form. The [comprehensions guide](/programming/python/comprehensions/) explains the syntax. Once the body needs a second step, like logging the rejects, I'd go with the loop, because each step gets its own line and reads top to bottom. The trade-off is a few extra lines, and on a filter this small either one does the job. ### Collecting Error Messages You can also append messages as you check a form. Collecting every problem into a list, instead of stopping at the first one, lets you show a user everything they need to fix in one go: ```python signup = { "name": "", "email": "bo@", "age": 15, } errors = [] if not signup["name"]: errors.append("name is required") if "." not in signup["email"]: errors.append("email looks wrong") if signup["age"] < 16: errors.append("must be 16 or over") for error in errors: print("-", error) ``` ```text # Program output: # - name is required # - email looks wrong # - must be 16 or over ``` Heads up: that email check is a toy, simplified on purpose to show how the messages pile up, so use a proper validator before you trust it with live signups. When the checks all pass, `errors` stays empty. An empty list counts as false in an `if` test, so `if errors:` tells you whether anything went wrong. ## Common Pitfalls and Debugging ### `AttributeError: 'NoneType' object has no attribute 'append'` This is the big one. It comes from assigning the result of `append()` back to your variable. The method returns `None`, so the assignment swaps your list for `None`, and the next `append()` fails: ```python items = ["a", "b"] items = items.append("c") print(items) items.append("d") ``` ```text # Program output: # None # Traceback (most recent call last): # ... # AttributeError: 'NoneType' object has no attribute 'append' ``` The traceback points at the second `append()`, and that's the trap, because the bug lives on the assignment line two lines earlier. Drop the assignment and call the method on its own line: ```python items = ["a", "b"] items.append("c") print(items) ``` ```text # Program output: # ['a', 'b', 'c'] ``` Chaining two calls, as in `items.append("c").append("d")`, hits the same error in one line, because the second call runs on the `None` the first one returned. The same message also turns up when a function you wrote forgets its `return`, so it hands back `None`, and you call `append()` on the result. ### `TypeError` When You Pass Two Items Pass two values and Python refuses, because `append()` adds one item per call. CPython words the error in its traceback like this: ```python items = [] items.append("a", "b") ``` ```text # Program output: # Traceback (most recent call last): # ... # TypeError: list.append() takes exactly one argument (2 given) ``` To add both, pass them to `extend()` inside a list: ```python items = [] items.extend(["a", "b"]) print(items) ``` ```text # Program output: # ['a', 'b'] ``` ### Appending the Same List Over and Over A list stores a reference to whatever you append, and it doesn't make a copy. So if you create one inner list outside a loop and append it three times, you've got three slots pointing at one list: ```python grid = [] row = [] for _ in range(3): grid.append(row) grid[0].append("x") print(grid) ``` ```text # Program output: # [['x'], ['x'], ['x']] ``` You changed row 0 and all three rows changed. Build a fresh inner list on each pass instead, and each slot gets its own: ```python grid = [] for _ in range(3): grid.append([]) grid[0].append("x") print(grid) ``` ```text # Program output: # [['x'], [], []] ``` We go deeper on copying and shared references in the [Python Lists guide](/programming/python/lists/#why-does-copying-a-list-catch-people-out). ### Strings and Tuples Have No `append()` Strings and tuples can't be changed after you create them, so neither type has an `append()` method. Calling it raises an `AttributeError` that names the type: ```python name = "Ada" name.append("!") ``` ```text # Program output: # Traceback (most recent call last): # ... # AttributeError: 'str' object has no attribute 'append' ``` For a string, build a new one with `name = name + "!"`. For a tuple, you get `'tuple' object has no attribute 'append'`, and the fix is either a new tuple or a switch to a list if the collection is meant to grow. ## `append()` vs `extend()` vs `+=` Three tools add to the end of a list, and the only thing you have to keep straight is how many items each one adds. | Tool | Adds | Passing [3, 4] | | `append(x)` | One item | One nested list | | `extend(xs)` | Each item of xs | Two items, 3 and 4 | | `items += xs` | Each item of xs | Two items, 3 and 4 | On a list, `+=` behaves like `extend()` and changes the same list in place, including the split-a-string surprise: ```python nums = [1, 2] nums += [3, 4] print(nums) nums += "ab" print(nums) ``` ```text # Program output: # [1, 2, 3, 4] # [1, 2, 3, 4, 'a', 'b'] ``` Choosing between the three comes down to how many items you've got: - For one item, use `append()`, since it's the only one of the three that never splits your item into pieces. - For several items, `extend()` and `+=` do the same job on a list. I'd pick `extend()` when other code holds a reference to the same list, because the method name tells the next reader the list is changing in place. - The trade-off is length: `+=` is shorter, and in a loop where nobody else touches the list, it's a perfectly good choice. To put an item somewhere other than the end, [`insert()`](/programming/python/lists/insert/) takes a position as well as the value, and the [Append, Extend, and Insert](/programming/python/lists/#append-extend-and-insert) section of the Lists guide shows all three side by side. ## Build a List One Item at a Time Use `append()` when you have one item to add to the end, and call it on its own line, because it returns `None`. When you have several items, pass them to `extend()` so each one lands as its own element. Then go build something small with it tonight, like a to-do list that grows one `append()` at a time. ## Frequently Asked Questions ### Does Python append() return a new list? Python list.append() changes the existing list and returns None, not a new list. That is why items = items.append(x) replaces your list with None. Call items.append(x) on its own line and keep using items. ### Can Python append() add several items in one call? Python list.append() adds one item per call and accepts exactly one argument. Use extend() or += to add several items separately. Passing a list to append() adds that whole list as one nested item. ### Is Python append() slow on a long list? Python list.append() stays fast on average as a list grows. CPython keeps spare space for new items, so most calls fill a free slot. An occasional call must resize the list, which takes more work. ### How do you add an item to the front of a Python list? To add one item to the front of a Python list, use items.insert(0, x). This puts x at index 0 and shifts the existing items along. For frequent additions to the front of a long collection, use collections.deque and appendleft(). ### Does Python append() copy the object you pass in? Python list.append() stores a reference to the object you pass; it does not copy the object. If you append a list and later change it, the change appears inside the outer list too. Append row.copy() when you need a separate shallow copy. ### How do you append to a Python dictionary? A Python dictionary has no append() method. Add a key-value pair with an assignment such as prices["tea"] = 3, or use update() for several pairs. If a dictionary value is a list, you can call append() on that list. ## Next Steps - [Python List remove() vs pop()](/programming/python/lists/remove-and-pop/) - taking items back out, by value or by position. - [Python Dictionaries](/programming/python/dictionaries/) - adding keys and values when a list is the wrong shape. --- ## Python Lists: Create, Slice, and Iterate URL: /programming/python/lists/ Cluster: Programming > Python A Python list stores items in order and lets you add, remove, or change them. Use a list for a sequence such as names, scores, or records that you want to process one at a time. Think of a shelf with numbered slots. Each slot has a position, but two slots can hold the same value. This guide covers the everyday operations and the copying mistakes worth catching early. For the wider learning path, see the [Python guide](/programming/python/). ## What Is a Python List? A Python list is an ordered, changeable collection of values. Ordered means each item has a position you can ask for. Changeable, or mutable in Python's own vocabulary, means you can replace, add, and remove items after the list exists. An index is a position, counted from zero. A slice is a range of positions. Mutation is any change made to the list itself rather than to a copy of it. A list holds references rather than the values themselves, which is why it accepts any mix of types and why copying needs a section of its own further down. Its closest neighbors are the tuple, an immutable sequence, and the [dictionary](/programming/python/dictionaries/), which stores items under names instead of positions. ## How Do You Create a Python List? Create a Python list with square brackets, or pass an iterable to `list()` to build a list from its items: ```python fruits = ["apple", "banana", "cherry"] empty = [] letters = list("hello") digits = list(range(5)) print(fruits) print(letters) print(digits) ``` ```text # Program output: # ['apple', 'banana', 'cherry'] # ['h', 'e', 'l', 'l', 'o'] # [0, 1, 2, 3, 4] ``` Multiplying a list by an integer repeats its contents, which is a convenient way to lay out a row of placeholders: ```python slots = [0] * 5 print(slots) ``` ```text # Program output: # [0, 0, 0, 0, 0] ``` That shortcut has one sharp edge involving nested lists, and it gets its own pitfall below. For simple values such as numbers and strings it behaves exactly as it reads. ## How Do You Access Items in a Python List? Access a Python list item with its index in square brackets. Indexes start at zero, so `fruits[0]` reads the first item and `fruits[3]` reads the fourth: ```python fruits = ["apple", "banana", "cherry", "date"] print(fruits[0]) print(fruits[3]) fruits[1] = "blueberry" print(fruits) ``` ```text # Program output: # apple # date # ['apple', 'blueberry', 'cherry', 'date'] ``` ### Counting Backwards A negative index counts from the right-hand end of the shelf, so `-1` is the last item. It saves writing the length arithmetic yourself: ```python fruits = ["apple", "banana", "cherry", "date"] print(fruits[-1]) print(fruits[-2]) ``` ```text # Program output: # date # cherry ``` ### Slices Take a Range A slice is written `[start:stop:step]` and always returns a new list. The `stop` position is excluded, which is why `[2:5]` gives three items rather than four: ```python numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] print(numbers[2:5]) print(numbers[:3]) print(numbers[7:]) print(numbers[::2]) print(numbers[::-1]) ``` ```text # Program output: # [2, 3, 4] # [0, 1, 2] # [7, 8, 9] # [0, 2, 4, 6, 8] # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] ``` Leaving a position out means "from the beginning" or "to the end". A step of `-1` walks the shelf right to left, which is the shortest way to reverse a list without changing the original. Slices are also forgiving where plain indexing is not. Asking for `numbers[50:60]` returns an empty list, while asking for `numbers[50]` raises an error. ## How Do You Count, Find, and Loop Through List Items? Use `len()` to count all items in a Python list, `count()` to count a particular value, and `in` to check whether a value is present. A `for` loop visits each item in order. ```python fruits = ["apple", "banana", "apple"] print(len(fruits)) # 3 print(fruits.count("apple")) # 2 print("banana" in fruits) # True for fruit in fruits: print(fruit) ``` Use `fruits.index("banana")` to find the first matching position. It returns `1` here and raises `ValueError` if the value is absent. Use `enumerate(fruits)` when a loop needs both the index and the item. ## How Do You Add or Remove Python List Items? Add Python list items with `append()`, `extend()`, or `insert()`. Remove an item by value with `remove()`, or by position with `pop()` or `del`. ### Append, Extend, and Insert Three methods add to a list, and the difference between the first two is the single most common mix-up in this area: ```python shelf = ["apple", "banana"] shelf.append("cherry") shelf.extend(["date", "elderberry"]) shelf.insert(1, "apricot") print(shelf) ``` ```text # Program output: # ['apple', 'apricot', 'banana', 'cherry', 'date', 'elderberry'] ``` The [`append()` method](/programming/python/lists/append/) adds exactly one item, even when that item is itself a list. The `extend` method loops over what you give it and adds each element separately: ```python a = [1, 2] b = [1, 2] a.append([3, 4]) b.extend([3, 4]) print(a) print(b) ``` ```text # Program output: # [1, 2, [3, 4]] # [1, 2, 3, 4] ``` The `insert` method takes a position first and the value second. Inserting at position 0 puts an item at the front, and every other item shifts one slot along, which is worth remembering when the list is long. The [Python list insert() guide](/programming/python/lists/insert/) shows how the index picks the spot, including negative and out-of-range positions. ### Remove, Pop, and Delete Removal comes in three flavors depending on whether you know the value, the position, or neither: ```python shelf = ["apple", "banana", "cherry", "banana", "date"] shelf.remove("banana") taken = shelf.pop(1) last = shelf.pop() del shelf[0] print(taken, last) print(shelf) ``` ```text # Program output: # cherry date # ['banana'] ``` The `remove` method deletes the first matching value and raises `ValueError` when there is none. The `pop` method deletes by position and hands the item back, defaulting to the last slot. The `del` statement deletes without returning anything, and `shelf.clear()` empties the list entirely. The [remove() vs pop() guide](/programming/python/lists/remove-and-pop/) covers the errors each one raises and the cases where `remove()` deletes an item you didn't expect. ## How Do You Sort or Reverse a Python List? Sort a Python list in place with `sort()`, or create a sorted copy with `sorted()`. To reverse the current order without sorting, use `reverse()` in place or `[::-1]` for a reversed copy. ```python scores = [3, 1, 4, 1, 5] ordered = sorted(scores) scores.sort(reverse=True) print(ordered) print(scores) ``` ```text # Program output: # [1, 1, 3, 4, 5] # [5, 4, 3, 1, 1] ``` Both accept a `key` function that says what to sort on, which is how you sort words by length or records by one field: ```python words = ["kiwi", "banana", "fig", "plum", "apple"] print(sorted(words, key=len)) ``` ```text # Program output: # ['fig', 'kiwi', 'plum', 'apple', 'banana'] ``` Python's sort is stable, which means items the key rates as equal keep the order they already had. Here `kiwi` stays ahead of `plum` because both are four letters long and `kiwi` appeared first in the input. Reversing follows the same in-place or new-list pattern. The `reverse` method flips the list in place, while `list(reversed(words))` and the `[::-1]` slice each build a new one. ## Why Can Changing a Copied List Affect the Original? Assigning a Python list to another variable does not copy it. Both names refer to the same list, so changes appear through either name. Even a shallow copy still shares nested objects, as the second example below shows: ```python original = [1, 2, 3] alias = original alias.append(4) print(original) ``` ```text # Program output: # [1, 2, 3, 4] ``` Three spellings make a genuine copy of the outer list, and they behave identically for values such as numbers and strings: ```python original = [1, 2, 3] first = original.copy() second = list(original) third = original[:] first.append(4) print(original, first) ``` ```text # Program output: # [1, 2, 3] [1, 2, 3, 4] ``` All three of those spellings make shallow copies. The new list is independent, but the objects inside it are still shared, so a nested list is reachable from both copies: ```python import copy grid = [[1, 2], [3, 4]] shallow = grid.copy() deep = copy.deepcopy(grid) shallow[0].append(99) print(grid) print(deep) ``` ```text # Program output: # [[1, 2, 99], [3, 4]] # [[1, 2], [3, 4]] ``` Reach for `copy.deepcopy` only when the nested objects need to be independent. It walks the whole structure, so it costs more than a slice, and on flat lists of numbers or strings it buys you nothing. ## Which Python List Operations Are Fast? In CPython, reading an item by index and appending at the end are generally fast. Inserting near the front or searching for a value takes more work as the list grows. CPython stores list references together with spare room for new items at the end. Reading or writing by index is a direct jump, so its cost does not grow with the list. Appending is cheap for the same reason, apart from the occasional resize when the spare room runs out. Inserting or deleting anywhere except the end has to shift every later element along by one slot, so the work grows with how much of the list sits after that point. Searching with `in` or `index` walks the list until it finds a match, so it grows the same way. Those costs lead to two practical habits. Build lists by appending rather than by inserting at position 0, and when you need to test membership repeatedly against a large collection, build a [set](/programming/python/lists/lists-vs-dictionaries-vs-sets/) once and test against that instead. For a queue where items leave from the front, `collections.deque` is designed for exactly that shape. ## A Worked Example: Tidying a Signup List Names arrive from a form with stray spacing, inconsistent capitals, blanks, and repeats, and the page needs a clean alphabetical roster. ```python raw = [" Ada ", "bob", "", "ADA", "Cleo", "bob "] names = [] for entry in raw: cleaned = entry.strip().title() if cleaned and cleaned not in names: names.append(cleaned) names.sort() print(names) ``` ```text # Program output: # ['Ada', 'Bob', 'Cleo'] ``` The loop trims spaces, fixes capitalization, and skips empty or repeated names. It appends each accepted name, then sorts the finished list once. The membership test is the line to watch as the data grows. On a signup form of a few hundred names it is invisible, and on a mailing list of a million it is the slowest thing in the script, because each test walks everything gathered so far. ```python raw = [" Ada ", "bob", "", "ADA", "Cleo", "bob "] seen = set() names = [] for entry in raw: cleaned = entry.strip().title() if cleaned and cleaned not in seen: seen.add(cleaned) names.append(cleaned) print(sorted(names)) ``` ```text # Program output: # ['Ada', 'Bob', 'Cleo'] ``` This version prints the same names, and once many different names pile up it usually runs much faster. The list still holds the order, and the set answers whether a name has already been seen with a hash lookup instead of a scan. Keeping a list and a set side by side like this is a normal, everyday pattern. ## Pitfalls and Debugging ### Removing Items While Looping Deleting from a list you are currently looping over skips elements, because the loop's internal position keeps advancing while the items shuffle down to meet it: ```python numbers = [1, 2, 4, 6, 7] for n in numbers: if n % 2 == 0: numbers.remove(n) print(numbers) ``` ```text # Program output: # [1, 4, 7] ``` The `4` survives because removing `2` moved it into the slot the loop had already passed. Nothing raises an error, which is what makes this one expensive to find. Build a new list with a [list comprehension](/programming/python/comprehensions/) instead, keeping only the values you want: ```python numbers = [1, 2, 4, 6, 7] numbers = [n for n in numbers if n % 2 != 0] print(numbers) ``` ```text # Program output: # [1, 7] ``` ### The Repeated Empty List Trap The repetition shortcut copies references, not objects. With numbers that is invisible, and with nested lists it produces three names for one list: ```python rows = [[]] * 3 rows[0].append(1) print(rows) rows = [[] for _ in range(3)] rows[0].append(1) print(rows) ``` ```text # Program output: # [[1], [1], [1]] # [[1], [], []] ``` The comprehension runs `[]` once per row, so each row is a separate list. Use the multiplication form for immutable fillers such as `0` or `None`, and the comprehension form whenever the filler is something you intend to change. ### A Mutable Default Argument Default arguments are evaluated once, when the function is defined. A list used as a default is therefore shared by every call that does not pass its own: ```python def add_item(item, items=[]): items.append(item) return items print(add_item("a")) print(add_item("b")) ``` ```text # Program output: # ['a'] # ['a', 'b'] ``` The fix is to default to `None` and create the list inside the body, so each call that needs one gets a fresh list: ```python def add_item(item, items=None): if items is None: items = [] items.append(item) return items print(add_item("a")) print(add_item("b")) ``` ```text # Program output: # ['a'] # ['b'] ``` ### IndexError and ValueError Two named errors come up constantly, and their messages say which mistake you made. Reading past the end of a list raises `IndexError: list index out of range`, which usually means a loop counter or an off-by-one calculation went past the length. Calling `remove` or `index` with a value the list does not hold raises `ValueError: list.remove(x): x not in list` or a similar message for `index`. Test with `in` first when the value is optional. ## When Should You Use Something Other Than a Python List? Use a Python dictionary for lookups by key, a set for unique values and repeated membership checks, a tuple for a fixed sequence, or a deque for frequent changes at both ends. Keep a list when positions and a changeable sequence are what you need. - When items are looked up by name rather than by position, a [dictionary](/programming/python/dictionaries/) stores the name with the value instead of making you remember which slot it went into. - When the only question is "have I seen this?", a set answers it without walking the collection and refuses duplicates for you. - When the collection must not change after it is built, a tuple says so in the type, and a tuple of hashable items can also be a dictionary key. - When items are added and removed at both ends, `collections.deque` is built for that traffic, while a list pays a shifting cost at the front. Our [list, dictionary, and set comparison](/programming/python/lists/lists-vs-dictionaries-vs-sets/) shows which collection fits each job. ## Frequently Asked Questions ### Are Python lists the same as arrays? Python lists can hold different types and grow as items are added, while typed arrays restrict their elements to one type. For compact numeric storage, Python offers array.array in the standard library; NumPy provides ndarray for numerical work. ### When should you use a Python tuple instead of a list? Use a Python tuple instead of a list when the sequence should stay fixed, such as a coordinate pair. A tuple containing only hashable objects can also be a dictionary key or set member. Nested mutable objects inside a tuple can still change. ### Can a Python list hold different types at once? A Python list can hold numbers, strings, other lists, and custom objects together. If each position represents a named field, a dictionary or small class may make the data easier to understand. ### What is the difference between Python list.sort() and sorted()? Python list.sort() reorders an existing list and returns None. The sorted() function returns a new sorted list without changing the original. Both accept key and reverse arguments, and sorted() can take any iterable. ### How do you remove duplicates from a Python list? For a Python list of hashable items, list(dict.fromkeys(items)) removes duplicates while keeping their first appearance. list(set(items)) also removes duplicates but does not preserve the original order. For unhashable items such as nested lists, build a new list and check whether each item is already present. ### Does a Python list have a maximum size? A Python list is limited by the interpreter and available memory. On a typical 64-bit build, sys.maxsize is 2**63 - 1, but a machine will run out of memory long before a list reaches that length. ### Why does Python list.append() return None? Python list.append() changes the existing list and returns None rather than a new list. Call items.append(x) on its own. Writing items = items.append(x) replaces the variable with None. ## Self-Check 1. What does `[1, 2, 3, 4][1:3]` evaluate to, and why is it two items rather than three? 2. After `a = [1, 2]` and `a.append([3, 4])`, what is `len(a)`? 3. Which of `sort` and `sorted` returns `None`, and what does the other return? 4. Why does `rows = [[]] * 3` followed by `rows[0].append(1)` change all three rows? 5. What does `numbers[50]` raise on a ten-item list, and what does `numbers[50:60]` return? 6. Why is inserting at position 0 more expensive than appending? 7. A function has `def tag(name, tags=[])`. What goes wrong on the second call, and what is the fix? ### Answers 1. `[2, 3]`. A slice includes the start position and excludes the stop position, so `1:3` covers indexes 1 and 2. 2. 3. The `append` method adds one item, and here that item happens to be a list, so `a` is `[1, 2, [3, 4]]`. 3. `sort` returns `None`. It reorders the list in place, while `sorted` leaves the original alone and returns a new list. 4. Because the repetition copies the reference three times. All three positions name one list, so appending through any of them is visible from all of them. 5. `IndexError`, and an empty list. Plain indexing must land on a real position, while slicing clamps to what exists. 6. Every later element shifts one slot along. Appending is amortized constant time because it usually writes into spare room at the end, with occasional resizes. 7. The second call sees the first call's list. The default is created once at definition time, so default to `None` and build the list inside the function. ## Where to Go Next Practice choosing between changing a list and making a copy. Then try the signup example with your own data. The difference between `append()` and `extend()`, and between assignment and copying, will come up often. --- ## Python List insert(): Add an Item at Any Position URL: /programming/python/lists/insert/ Cluster: Programming > Python The `insert()` method adds one item to a [Python list](/programming/python/lists/) at the position you choose. You hand it an index and a value, and the value cuts in right before whatever currently sits at that index: ```python steps = ["wash", "dry"] steps.insert(1, "rinse") print(steps) ``` ```text # Program output: # ['wash', 'rinse', 'dry'] ``` Nothing got overwritten. The `"dry"` item moved from index 1 to index 2 to make room. Inserting near the front of a long list takes more work because more items have to move. ## Syntax and Basic Usage ```python items.insert(i, x) # puts x before the item at index i ``` - i (required integer): the position the new item should end up at. Negative indexes count back from the end, and an index outside the list is clamped to the nearest end. - x (required, any type): the item to add, always as one item. - Returns: `None`. The list itself is what changes. - Raises: `TypeError` when `i` is not an integer or either argument is missing. The index always comes first and the value second, so say it in your head the first few times you type it. Here are the front, the middle and the end of the same list: ```python letters = ["b", "c", "d"] letters.insert(0, "a") print(letters) letters.insert(2, "x") print(letters) letters.insert(len(letters), "z") print(letters) ``` ```text # Program output: # ['a', 'b', 'c', 'd'] # ['a', 'b', 'x', 'c', 'd'] # ['a', 'b', 'x', 'c', 'd', 'z'] ``` Inserting at `len(letters)` lands the item at the end, because there's nothing at that index for it to go in front of. That works, and it earns its place when the position comes from a calculation that sometimes lands on the end. When you always mean the end, use [`append()`](/programming/python/lists/append/), because it says so plainly. ## How Does Python insert() Use the Index? Python `insert(i, x)` puts `x` before the item at index `i`. Index `0` adds to the front, and `len(items)` adds to the end. Negative indexes count back from the end. ### Negative Indexes A negative index counts from the end, just like it does when you read an item, and the new item still goes in before that position. So `insert(-2, x)` puts `x` before the second-to-last item: ```python letters = ["a", "b", "c"] letters.insert(-2, "x") print(letters) ``` ```text # Program output: # ['a', 'x', 'b', 'c'] ``` The same rule sends `insert(-1, x)` in before the last item. Use `append()` if the new item should come after the last item. ### What Happens If Python insert() Gets an Out-of-Range Index? Python `insert()` accepts an ordinary integer index outside the list without raising `IndexError`. An index past the end is treated as the end, and a negative index past the start is treated as the front: ```python letters = ["a", "b", "c"] letters.insert(99, "end") letters.insert(-99, "start") print(letters) ``` ```text # Program output: # ['start', 'a', 'b', 'c', 'end'] ``` That's the opposite of [pop()](/programming/python/lists/remove-and-pop/), which raises `IndexError` on a bad index. So an off-by-one index can land the item at an end without a peep. CPython still raises `OverflowError` if the integer is too large for its internal index type, as with `insert(10**100, x)`. For the loops and other language basics behind these examples, see our [guide to learning Python](/programming/python/). ## Practical Examples ### Adding a Header Row Index 0 is probably the spot you'll reach for most often. It puts a heading in front of rows you've already collected, right before you print them or write them out: ```python rows = [["Ada", 36], ["Bo", 29]] rows.insert(0, ["name", "age"]) for row in rows: print(row) ``` ```text # Program output: # ['name', 'age'] # ['Ada', 36] # ['Bo', 29] ``` ### Keeping a List Sorted To drop a new value into an already sorted list and keep it sorted, you need to find the right index first. The standard library's `bisect` module finds that index and inserts for you: ```python import bisect scores = [52, 67, 81, 94] bisect.insort(scores, 75) bisect.insort(scores, 40) print(scores) ``` ```text # Program output: # [40, 52, 67, 75, 81, 94] ``` For a list that's already sorted, I'd recommend `insort()` over a search loop you write yourself, because it finds the spot with a binary search and inserts in the same call. The trade-off is that the list has to be sorted to begin with, or the position it picks won't mean anything. ## Common Pitfalls and Debugging ### `insert(-1, x)` Does Not Add to the End Index `-1` reads as "the last one", so you'd expect `insert(-1, x)` to add at the end. It inserts before the last item, and Python raises no error, so the bug only shows when you look at the order: ```python letters = ["a", "b", "c"] letters.insert(-1, "x") print(letters) letters.append("y") print(letters) ``` ```text # Program output: # ['a', 'b', 'x', 'c'] # ['a', 'b', 'x', 'c', 'y'] ``` When you mean the end, use `append()`, as the second call does. ### `TypeError: 'str' object cannot be interpreted as an integer` Swap the two arguments and Python tries to use your value as the index. CPython's error names the type of whatever landed in the index slot: ```python tasks = ["plan", "build"] tasks.insert("ship", 2) ``` ```text # Program output: # Traceback (most recent call last): # ... # TypeError: 'str' object cannot be interpreted as an integer ``` Put the index first and the value second, as in `tasks.insert(2, "ship")`. A float index fails the same way, so `insert(1.0, x)` raises the same error with `'float'` in it. Check what the computed position means before converting it: `int()` truncates toward zero, which can conceal a calculation error. ### `TypeError: insert expected 2 arguments` Pass only the value and `insert()` has no position to work with. CPython reports how many arguments it got: ```python tasks = ["plan", "build"] tasks.insert("ship") ``` ```text # Program output: # Traceback (most recent call last): # ... # TypeError: insert expected 2 arguments, got 1 ``` If you meant the end of the list, you wanted `append()`. Otherwise, add the index in front of the value. ### Inserting a List Nests It Like `append()`, `insert()` adds exactly one item, so handing it a list puts the whole list in as a single nested item: ```python letters = ["a", "e"] letters.insert(1, ["b", "c", "d"]) print(letters) ``` ```text # Program output: # ['a', ['b', 'c', 'd'], 'e'] ``` To splice several items in at one position, assign them to an empty slice. The slice `letters[1:1]` covers no items, so nothing gets replaced: ```python letters = ["a", "e"] letters[1:1] = ["b", "c", "d"] print(letters) ``` ```text # Program output: # ['a', 'b', 'c', 'd', 'e'] ``` ### Inserting at the Front of a Long List Every insert has to slide the items after it one place along, so `insert(0, x)` moves every item already in the list, every time you call it. It's the Turnpike merge at rush hour: one car noses in up front, and every car behind it has to shuffle back. On a short list you'll never notice. When a long list keeps growing at the front, the Python docs point you to `collections.deque`, whose `appendleft()` is built for that job: ```python from collections import deque recent = deque(["page2", "page1"]) recent.appendleft("page3") print(recent) ``` ```text # Program output: # deque(['page3', 'page2', 'page1']) ``` Don't swap a working list for a `deque` on a hunch, though. Make the switch when the list is big and the front inserts happen often. ## `insert()` vs `append()` vs Slice Assignment Three tools put new items into a list, and they split on where the items go and how many you can add per call. | Tool | Where | How many | | `insert(i, x)` | Before index i | One item | | `append(x)` | The end | One item | | `items[i:i] = xs` | Before index i | Each item of xs | Pick by where the items go and how many there are: - For one item at the end, `append()` is the clear choice. - For one item with a real position, like a header row at index 0, use `insert()`. - When several items belong at the same spot, I'd recommend slice assignment over a loop of `insert()` calls, because one assignment keeps them in their original order, while repeated `insert(1, x)` calls land them in reverse. The trade-off is readability, since `items[i:i] = xs` is less obvious to a beginner than a method name. For adding several items to the end, `extend()` is the usual choice, and we compare it with the other two in the [Append, Extend, and Insert](/programming/python/lists/#append-extend-and-insert) section of the Lists guide. ## Choose the Right Position for Your New Item Use `insert(i, x)` when an item has to go somewhere other than the end, and remember that it lands before the item at `i`. For the end of the list, reach for `append()` instead, and watch for an out-of-range index, because `insert()` won't complain about it. Run `insert(-1, x)` on a three-item list once and watch it land one spot early, and that rule will stay with you. ## Frequently Asked Questions ### Does Python insert() replace the item at the chosen index? Python list.insert() adds a new item without replacing the item at that index. The existing item and everything after it move one position to the right. To replace an item instead, assign to its index, as in items[2] = x. ### Why does Python insert() not raise IndexError? Python list.insert() moves an out-of-range position to the nearest end of the list instead of raising IndexError. This applies to integers that fit the interpreter's index type; extremely large integers can raise OverflowError in CPython. ### How do you insert at the end of a Python list? To add one item to the end of a Python list, use items.append(x). The call items.insert(len(items), x) also works. On a nonempty list, items.insert(-1, x) puts the new item before the last item instead. ### Can Python insert() add more than one item? Python list.insert() adds exactly one item per call. Passing a list inserts it as one nested item. To insert several items at one position, assign to an empty slice, as in items[1:1] = ["b", "c"]. ### What does Python insert() return? Python list.insert() returns None and changes the list in place. Writing items = items.insert(0, x) replaces your list with None, so call insert() on its own line. ### Is Python insert() slower than append()? Inserting near the front of a Python list takes more work than appending because insert() must shift the later items. Inserting at the end does not require that shift. Use append() when you always want to add at the end. ### Does Python insert() work on strings or tuples? Python strings and tuples have no insert() method because they cannot change in place. Calling insert() on either raises AttributeError. Build a new string with slicing and +, or convert the sequence to a list before inserting. ## Next Steps - [Python Dictionaries](/programming/python/dictionaries/) - storing values under names when position does not matter. --- ## Lists vs Dictionaries vs Sets in Python URL: /programming/python/lists/lists-vs-dictionaries-vs-sets/ Cluster: Programming > Python Use [a list](/programming/python/lists/) to keep items in order, a dictionary to look up values by key, and a set to keep unique values or check membership. The right choice depends on what your program needs to do with the data. A shopping list can contain bread twice. A price dictionary stores one price under each product name. A set tells you which products are in stock, without keeping a count. Those differences matter more than which collection is fastest. New to the language? Start with the [Python guide](/programming/python/) for variables and loops, then use these examples to choose a collection. ## What Are Python Lists, Dictionaries, and Sets? Python lists store items in order, dictionaries map keys to values, and sets store unique values without an order guarantee. All three let you add and remove entries. A list is an ordered, changeable sequence written with square brackets. Every item has a position, positions are how you reach items, and the same value may appear as many times as you like. A dictionary is a collection of key and value pairs written with braces. Every value is stored under a key you chose, keys are how you reach values, and each key appears at most once. A set is an unordered collection of unique values, also written with braces but with no colons. A set has no positional indexes. It supports membership checks and operations such as finding values shared by two sets. Adding an existing value changes nothing. ```python arrivals = ["Ada", "Bo", "Ada"] scores = {"Ada": 91, "Bo": 78} attended = {"Ada", "Bo"} print(arrivals, len(arrivals)) print(scores["Ada"]) print("Cleo" in attended) ``` ```text # Program output: # ['Ada', 'Bo', 'Ada'] 3 # 91 # False ``` Those three lines are the whole comparison in miniature. The list kept the repeat because arrivals repeat, the dictionary attached a number to a name, and the set answered a yes or no question about membership. ## Quick Comparison The behaviors below are what differ between the three types. | Behavior | List | Dictionary | Set | | Keeps insertion order | Yes | Yes, since 3.7 | No | | Allows duplicates | Yes | Values only | No | | Reached by | Position | Key | Membership only | | Membership search cost | Grows with length | Flat, on average | Flat, on average | | Contents must be hashable | No | Keys only | Yes | | Stores | The item | Key and value | Unique items | | Literal for an empty one | Square brackets | Braces | Written set() | Python dictionaries preserve insertion order from Python 3.7 onward. Set membership and dictionary key lookups are fast on average, but hash collisions can make them slower. ## Which Python Collections Preserve Order? Python lists and dictionaries preserve insertion order; sets do not guarantee an order. Lists also let you access items by position or take a range with a slice. Adding or deleting items can change those positions. ```python log = ["opened", "edited", "saved"] print(log[0], log[-1]) print(log[1:]) ``` ```text # Program output: # opened saved # ['edited', 'saved'] ``` A [Python dictionary](/programming/python/dictionaries/) remembers the order keys were added, and that order is for iterating, not for reaching things. There is no `scores[0]` unless `0` happens to be a key you created. A set stores no order at all. It may print in a repeatable order inside one run, and nothing in the language promises that order between runs or between versions. Sort a set before comparing it to anything a person will read. ```python attended = {"Cleo", "Ada", "Bo"} print(sorted(attended)) ``` ```text # Program output: # ['Ada', 'Bo', 'Cleo'] ``` The question to ask is whether the sequence is part of the data. Events in time, steps in a recipe, and rows from a file all carry order. A roster of who has permission does not. ## How Do Python Lists, Dictionaries, and Sets Handle Duplicates? Python lists keep duplicate values, sets remove duplicates, and dictionaries require unique keys but allow repeated values. Use a list when repeated entries matter, such as several votes for the same choice. ```python votes = ["yes", "no", "yes"] tally = {"yes": 2, "no": 1} options = {"yes", "no", "yes"} print(len(votes), len(options)) print(tally["yes"]) ``` ```text # Program output: # 3 2 # 2 ``` A list keeps every repeat, which is correct when the repeat is a fact. Three votes are three votes, and collapsing them loses the count. A set discards repeats on the way in, silently and immediately. That is exactly right for the set of options available and exactly wrong for the votes cast. A dictionary sits between the two. Keys are unique, values are not, and the counting pattern turns a stream with repeats into a mapping from each distinct value to how often it appeared. ## Which Python Collection Is Best for Lookups? In Python, use a set for repeated membership checks and a dictionary to retrieve a value by key. A list works well for access by position, but finding a value may require checking every item. Sets and dictionaries avoid that full scan in the average case. ```python import timeit setup = "big_list = list(range(200_000)); big_set = set(big_list)" list_time = timeit.timeit("199_999 in big_list", setup=setup, number=100) set_time = timeit.timeit("199_999 in big_set", setup=setup, number=100) print(f"list: {list_time:.4f}s") print(f"set: {set_time:.6f}s") ``` Exact timings and the ratio vary between runs and between machines. The shape is what carries over. A worst-case list search on two hundred thousand items ordinarily takes far longer than the set lookup, and the gap widens as the collection grows. Hash collisions can slow set and dictionary lookups. Building a set also takes time and memory, so converting a short list for a single check may not help. Measure the code if performance matters. The other half of lookup is what comes back. A set tells you only whether the value is there, while a dictionary tells you what is attached to it. When the answer to "is this in here?" is immediately followed by "and what is its price?", the dictionary was the right container. ## What Can Python Lists, Dictionaries, and Sets Contain? Python lists and dictionary values can hold any object. Set members and dictionary keys must be hashable, such as strings, numbers, or tuples whose contents are all hashable. Lists and dictionaries themselves are not hashable. ```python pairs = [("London", "SW1"), ("Leeds", "LS1")] lookup = {pair: index for index, pair in enumerate(pairs)} unique = set(pairs) print(lookup[("Leeds", "LS1")]) print(len(unique)) ``` ```text # Program output: # 1 # 2 ``` A list can contain other lists, but a set of lists raises `TypeError: unhashable type: 'list'`. Converting an inner list to a tuple works only if every item inside that tuple is also hashable. A tuple that still contains a list remains unhashable. Tuples of hashable values work well for compound keys, such as a city and postal code used together to find a record. ## The Same Job in All Three A door log records each arrival, including repeat visits. Keep the original list to see who arrived first and last, build a dictionary to count visits, and use a set to find who attended. ```python entries = ["Ada", "Bo", "Ada", "Cleo", "Bo", "Ada"] print(entries[0], entries[-1]) print(len(entries)) ``` ```text # Program output: # Ada Ada # 6 ``` The list is the log itself. It answers "who was first?", "who was last?", and "how many entries were there?", and none of those questions survives de-duplication. ```python entries = ["Ada", "Bo", "Ada", "Cleo", "Bo", "Ada"] visits = {} for name in entries: visits[name] = visits.get(name, 0) + 1 print(visits) print(visits["Ada"]) ``` ```text # Program output: # {'Ada': 3, 'Bo': 2, 'Cleo': 1} # 3 ``` The dictionary counts each person's visits. It keeps one key per person and stores the count as the value. ```python entries = ["Ada", "Bo", "Ada", "Cleo", "Bo", "Ada"] expected = {"Ada", "Bo", "Dev"} present = set(entries) print(sorted(present)) print(sorted(expected - present)) print(sorted(present & expected)) ``` ```text # Program output: # ['Ada', 'Bo', 'Cleo'] # ['Dev'] # ['Ada', 'Bo'] ``` The set answers "who showed up at all?", and then answers two questions neither of the others can express in one line. Who was expected and never arrived is a difference, and who was both expected and present is an intersection. Same data, three containers, three different capabilities. The containers differ in what they can say as well as in how fast they say it. ## What Choosing Wrong Actually Costs ### A List Doing a Set's Job A list used to track values already seen can become slow. It works perfectly on test data and degrades quietly, because every membership test walks everything gathered so far. ```python incoming = ["a", "b", "a", "c", "b"] seen = [] for item in incoming: if item not in seen: seen.append(item) print(seen) ``` ```text # Program output: # ['a', 'b', 'c'] ``` Nothing is wrong with that output. The cost is that, in the worst case, processing a million distinct items does roughly half a trillion comparisons. Keep a set for membership checks alongside the list, or use `list(dict.fromkeys(incoming))` to remove duplicates from hashable values while keeping their first-seen order. ### A Dictionary Doing a List's Job A dictionary keyed by `0`, `1`, and `2` is a list wearing a disguise. It costs more memory, it cannot be sliced, sorting it means rebuilding it, and the keys can drift out of sequence when an entry is deleted. This one is more expensive to undo than the first, because every read site in the code was written against key access and the keys may have been serialized into stored data. Catch it at design time by asking whether any key means something other than "the position of this item". ### Losing Order and Repeated Values in a Set Converting a list to a set loses its original order and repeated values. Both are discarded at the moment of conversion, and neither can be recovered from the set afterwards. The damage shows up late, usually as an output whose sequence changes for no visible reason, and the repair is to go back to whatever produced the data. Convert to a set at the point where a question is about membership, not as a general-purpose tidy-up. ## Should You Use a Python List, Dictionary, or Set? Choose a Python list for positions and repeated entries, a dictionary for key-value pairs, and a set for unique values and membership checks. You can combine collections when one alone does not cover the job. Choose a list when: - The sequence is part of the data, such as events, steps, or rows read from a file. - Repeats are facts rather than noise, such as votes cast or entries in a log. - You reach items by position, or take ranges of them with slices. - The contents include things that cannot be hashed, such as other lists. Choose a dictionary when: - Each item has a natural name or identifier, and you reach it by that name. - Something is attached to each unique thing, such as a count, a price, or a record. - You are counting or grouping, such as visits per person or customers per city. - The data arrived as JSON, where objects map onto dictionaries directly. Choose a set when: - The only question is whether a value is present. - Duplicates are meaningless and should disappear on the way in. - You want to compare two collections with union, intersection, or difference. - Nothing needs to be attached to each member and no order needs keeping. When two of those lists describe your case, use two containers. A list beside a set is the standard answer to "ordered, and de-duplicated", and it is a design rather than a compromise. ## Choose a Collection for the Job Ask what question the collection has to answer, not what the data happens to look like. Order and repeats point to a list, a name with something attached points to a dictionary, and a plain yes or no about membership points to a set. ## Frequently Asked Questions ### Can Python lists, dictionaries, and sets be used together? Python lists, dictionaries, and sets can be used together. For example, keep a list of arrivals and a set of names already seen. The list preserves arrival order, while the set makes repeated membership checks quick. ### Is a Python set a dictionary without values? A Python set stores unique values without pairing them with separate values, unlike a dictionary. Both sets and dictionaries use hashing to find entries, but they are different types with different methods. Set members and dictionary keys must be hashable. ### Are sets ordered in modern Python? Python sets do not guarantee insertion order. Dictionaries have guaranteed insertion order since Python 3.7, but sets have not. Use sorted(my_set) when you need a predictable display order and the items can be compared. ### How do you convert between Python lists, dictionaries, and sets? To convert Python collections, use set(items) for unique values, list(my_set) for a list, or dict(pairs) for key-value pairs. A set does not preserve the original order. Converting a dictionary with list(d) or set(d) keeps its keys, not its values. ### Which uses less memory in Python: a list, dictionary, or set? For the same number of entries, a CPython list usually uses less container memory than a set or dictionary. Sets and dictionaries need extra space for fast lookups. The exact amount varies, and sys.getsizeof() counts the container separately from the objects it refers to. ### When should you use a Python tuple instead of a list? A Python tuple is an immutable sequence, useful for a fixed group such as a coordinate pair. Unlike a list, a tuple cannot have items added, removed, or replaced. A tuple can be a dictionary key or set member only when all its contents are hashable. ## Next Steps - [List and Dict Comprehensions in Python](/programming/python/comprehensions/) - build any of these from another iterable in one expression. --- ## Python List remove() vs pop(): Delete by Value or by Index URL: /programming/python/lists/remove-and-pop/ Cluster: Programming > Python When you want one item out of a [Python list](/programming/python/lists/), you've got two methods to pick from, and what separates them is what you know about the item going in. The `remove()` method finds it by its value, while `pop()` takes it by position and hands it back to you: ```python queue = ["Ada", "Bo", "Cleo", "Dev"] queue.remove("Bo") served = queue.pop(0) print(served) print(queue) ``` ```text # Program output: # Ada # ['Cleo', 'Dev'] ``` The first call went looking for `"Bo"` by value and deleted it. The second call didn't care what sat at index 0, it just pulled out `"Ada"` and gave it back so you could keep it in `served`. Keep that split between value and position in mind, because several of the traps below come straight out of it. ## How Does Python remove() Delete a List Item? Python's `list.remove()` method starts at the front of the list and deletes the first item that matches the value you pass. It changes the list in place, so there's no new list for you to catch. ```python list.remove(x) # deletes the first matching item and returns None ``` - x (required, any type): the value to look for. - Returns: `None`. The list itself is what changes. - Raises: `ValueError` when the search reaches the end of the list without a match. For each item, Python first asks whether it's the very same object you passed, and if it isn't, whether the two compare equal with `==`. In everyday code, the first item equal to `x` goes, even if it's a different object. The [NaN pitfall](#equal-values-count-as-a-match) below is where that first step changes the result. ```python tags = ["python", "draft", "lists", "draft"] tags.remove("draft") print(tags) ``` ```text # Program output: # ['python', 'lists', 'draft'] ``` Only the first `"draft"` got deleted. The second copy is still sitting right there at the end, and that surprises plenty of people who figured `remove()` would clear out every copy. When you need them all gone, the [Removing Every Match](#removing-every-match) example shows you how. ## How Does Python pop() Remove and Return a List Item? Python's `list.pop()` removes an item by position and returns that item. You give it an index, and it pulls out whatever sits there and returns it, so the value is still yours to use after it leaves the list. ```python items.pop() # last item items.pop(i) # item at index i ``` - i (optional integer): the position to remove. It defaults to `-1`, the last item, and negative indexes count back from the end. Pass a whole number, and pass it positionally: both `pop("2")` and `pop(index=0)` raise `TypeError`. - Returns: the removed item. - Raises: `IndexError` when the list is empty or the index is out of range. ```python history = ["open", "edit", "format", "save"] last = history.pop() first = history.pop(0) second_last = history.pop(-2) print(last, first, second_last) print(history) ``` ```text # Program output: # save open edit # ['format'] ``` Pay attention to how the positions move between calls. After the first two pops the list is `["edit", "format"]`, so by then `-2` points at `"edit"`, even though it pointed at `"format"` when you started. Each pop can change what an index points at, so always read an index against the list as it stands right now. ## Why Does Python remove() Return None? Python `remove()` returns `None` because its job is to change the list, not return the deleted item. Methods such as `append()` and `sort()` follow the same convention. Use `pop()` when you need to keep the removed item. ```python cart = ["milk", "eggs", "bread"] result = cart.remove("eggs") print(result) print(cart) ``` ```text # Program output: # None # ['milk', 'bread'] ``` So if you write `cart = cart.remove("eggs")`, you've thrown away your list and kept the `None`. Python won't say a word on that line, and the error only shows up when a later line tries to use `cart` as a list, which sends you hunting for the bug in the wrong place. If you are still learning conditions and loops, the [Python basics guide](/programming/python/) gives you a starting point. ## Practical Examples ### An Undo Stack Pair [`append()`](/programming/python/lists/append/) with a bare `pop()` and a list turns into a stack, where the last thing in is the first thing out. Picture the plate stack at a diner: the last plate the dishwasher sets on top is the first one the next server grabs. An editor's undo history works the same way: ```python undo_stack = [] undo_stack.append("typed 'Hello'") undo_stack.append("made text bold") undo_stack.append("deleted a line") while undo_stack: action = undo_stack.pop() print("undo:", action) ``` ```text # Program output: # undo: deleted a line # undo: made text bold # undo: typed 'Hello' ``` The `while undo_stack:` test is what keeps this loop safe. An empty list counts as false, so the loop quits the moment the stack runs out and `pop()` never gets called on an empty list. ### Removing Every Match Because `remove()` quits after the first match, clearing out every copy of a value takes a different tool. My pick is a list comprehension that keeps everything except the value you're getting rid of: ```python tags = ["draft", "python", "draft", "lists"] tags = [tag for tag in tags if tag != "draft"] print(tags) ``` ```text # Program output: # ['python', 'lists'] ``` That line builds a brand-new list and points the name `tags` at it. If another variable still points at the old list and has to see the change too, assign into a full slice with `tags[:] = [...]`, which swaps out the contents of that same list object. The [comprehensions guide](/programming/python/comprehensions/) covers the filtering syntax in full. ## Common Pitfalls and Debugging ### ValueError When the Value Is Missing Hand `remove()` a value the list doesn't hold and you get a `ValueError`. CPython, the standard interpreter, prints it as `ValueError: list.remove(x): x not in list`, which is the exact line to paste into a search bar if you ever forget what it means. When the value might legitimately be missing, check for it first with `in`: ```python tags = ["python", "lists"] if "draft" in tags: tags.remove("draft") print(tags) ``` ```text # Program output: # ['python', 'lists'] ``` When the value is usually there, the `in` test and `remove()` both search from the front until they reach it, so you pay for that search twice. A `try` block skips the separate check and only catches the error when the value turns out to be missing: ```python tags = ["python", "draft", "lists"] try: tags.remove("draft") except ValueError: print("no draft tag to remove") print(tags) ``` ```text # Program output: # ['python', 'lists'] ``` Both versions handle a missing value correctly, and on a short list the extra search rarely matters. I'd pick whichever one reads better in that spot and not lose any sleep over it. ### IndexError: Pop from Empty List Call a bare `pop()` on an empty list and you get an `IndexError`, and an index past either end gets you the same error type. CPython prints `IndexError: pop from empty list` for the first case and `IndexError: pop index out of range` for the second. Because an empty list counts as false, the guard is short: ```python jobs = [] if jobs: next_job = jobs.pop() else: print("queue is empty") ``` ```text # Program output: # queue is empty ``` ### Removing While You Loop Deleting from a list while a `for` loop walks over it can make the loop skip items. Each deletion slides every later item down one slot, right underneath the loop, and the loop carries on counting as if nothing moved: ```python numbers = [2, 4, 5, 6] for number in numbers: if number % 2 == 0: numbers.remove(number) print(numbers) ``` ```text # Program output: # [4, 5] ``` The `4` survived because it slid into a slot the loop had already finished with. No single line in that code is wrong on its own, which is exactly why this bug is so annoying to track down. Build a filtered list with a comprehension instead, as in the example above, or loop over a copy with `for number in numbers[:]:`. The [Python Lists guide](/programming/python/lists/#removing-items-while-looping) walks through the bug step by step. ### Equal Values Count as a Match An equal value counts as a match whatever its type, because `remove()` never checks types itself. In Python `True == 1` and `1 == 1.0` are both true, and that catches people out in mixed lists: ```python answers = [True, 1, 1.0] answers.remove(1) print(answers) ``` ```text # Program output: # [1, 1.0] ``` You asked for the integer and lost the boolean at index 0 instead. Don't bother reaching for `index()` to fix it, because `index()` finds items exactly the same way `remove()` does. When the type matters, find the position yourself with `enumerate()` and a type check, then pop it: ```python answers = [True, 1, 1.0] for position, answer in enumerate(answers): if type(answer) is int and answer == 1: answers.pop(position) break print(answers) ``` ```text # Program output: # [True, 1.0] ``` The `break` ends the loop straight after the pop, so the items shifting down never get the chance to confuse it. NaN is the odd case that runs the other way. A float NaN isn't equal to any built-in number, itself included, so passing a fresh `float("nan")` to `remove()` raises `ValueError` even when a NaN is sitting right there in a list of ordinary numbers. Pass the exact NaN object that's sitting in the list, though, and `remove()` finds it. That's the same-object step from [Deleting by Value](#deleting-by-value) doing its job, and `in` checks a list the same way. ### Popping from the Front of a Long List Popping from the end is fast at any size. Popping from index 0 forces every remaining item to shift down one place, so a queue that keeps calling `pop(0)` gets slower as it grows. Think of a deli counter where the whole line takes a step forward every time a number gets called, which nobody minds with four people waiting and everybody minds with a line out the door. The Python tutorial points you to `collections.deque` for this job: ```python from collections import deque line = deque(["Ada", "Bo", "Cleo"]) line.append("Dev") print(line.popleft()) print(line) ``` ```text # Program output: # Ada # deque(['Bo', 'Cleo', 'Dev']) ``` For a short list the difference rarely matters, so don't go rewriting working code over it. Reach for `deque` when the queue is long or the loop runs often. ## Remove, Pop, Del, and Clear Compared You've got four common ways to delete from a list, and they split on two things: what each one targets, and what it hands back to you. | Tool | Deletes | Returns | Error | | `remove(x)` | First matching item | None | ValueError if absent | | `pop(i)` | One index, last by default | The item | IndexError if out of range | | `del items[i]` | One index or a slice | Nothing | IndexError on a bad single index | | `clear()` | Every item | None | Nothing to catch | The one to remember for ranges is the `del` statement, because it's the only one of the four that takes a slice: ```python scores = [90, 72, 85, 64, 99] del scores[1:3] print(scores) scores.clear() print(scores) ``` ```text # Program output: # [90, 64, 99] # [] ``` ## Choose a Removal Method by Value or Position When you know the value, use `remove()`, and when you know the position or need the item back afterwards, use `pop()`. For a range, reach for `del` with a slice. For every copy of a value, skip `remove()` altogether and rebuild the list with a comprehension, since calling it inside a `for` loop over the same list is how the skipped-item bug from earlier happens. ## Frequently Asked Questions ### Does Python remove() delete every matching list item? Python list.remove() deletes only the first matching item, leaving any later copies in the list. To delete every copy, build a new list with a comprehension that keeps only the items you want. ### What does Python list.pop() return without an index? Python list.pop() removes and returns the last item when you omit the index. You can save that item in a variable, as in last = items.pop(). Calling pop() on an empty list raises IndexError. ### Is Python pop() faster than remove() on a long list? Popping the last item from a Python list is generally faster than searching for a value with remove(). Removing near the front takes more work because later items must shift. Calling pop(0) has that shifting cost too. ### How do you delete a Python list item by index without returning it? To delete a Python list item by index without returning it, use del items[2]. The del statement also accepts a slice, so del items[1:3] deletes a range. Neither remove() nor pop() can delete a range in one call. ### Can Python list.pop() take a default value? Python list.pop() does not accept a default value. It accepts one optional index and raises IndexError if that position does not exist. Dictionary pop() is different: dict.pop(key, default) returns the default when the key is missing. ### Does Python pop() work on tuples and strings? Python tuples and strings have no pop() or remove() method because they cannot change in place. Calling either method raises AttributeError. Build a new tuple or string without the unwanted item, or convert to a list first. ## Next Steps - [Python List insert()](/programming/python/lists/insert/) - adding one item at a position you choose. - [Python Dictionaries](/programming/python/dictionaries/) - where pop() takes a key and a default instead of an index. - [Lists vs Dictionaries vs Sets in Python](/programming/python/lists/lists-vs-dictionaries-vs-sets/) - when a set is the better home for values you delete by name. --- ## SQL Comparisons URL: /programming/sql/comparisons/ Cluster: Programming > Sql Pick the engine from where the data lives and who writes to it. Choose SQLite when writes stay local, PostgreSQL for detailed data models, MySQL when your stack or host already targets it, and MariaDB only when the stack names MariaDB support. All four store relational data and understand familiar SQL; their operating models differ. ## Compare the Workload First Write down where the database will run and who will write to it. SQLite runs inside the application and stores a database in a local file, while MySQL, PostgreSQL, and MariaDB normally run as client-server systems that accept connections from applications. Then check the workload with these practical questions: - How many application processes or machines write at the same time? - Does the data stay on one device or serve a networked application? - Do reports need advanced joins, window functions, or specialized types? - Which engine does the framework, host, or managed provider support directly? - Can the team back up, restore, monitor, and upgrade the chosen engine? An existing supported database is usually safer than a migration made for marginal feature gains. For a new project, choose the smallest operational model that still meets the expected concurrency and data requirements. ## MySQL vs PostgreSQL vs SQLite vs MariaDB | Database | Typical Use | Strength and Choice | | MySQL | Web applications and managed hosting | Broad support; choose when the stack already targets MySQL | | PostgreSQL | Server applications and detailed data models | Rich types and constraints; choose for advanced relational work | | SQLite | Local apps, mobile, embedded data, tests | Single-file deployment; choose when writes can stay local | | MariaDB | MySQL-family web and server applications | MySQL protocol compatibility; choose with explicit MariaDB support | PostgreSQL fits applications that use detailed constraints, specialized data types, extensions, or complex reporting. MySQL fits conventional web stacks with mature hosting and framework support. MariaDB fits similar MySQL-family workloads when the host and application explicitly support it. SQLite is different because there is no separate database server. It works well for local application state, embedded devices, tests, command-line tools, and many lower-write websites. SQLite allows many readers but only one writer at a time, so high write concurrency can require a client-server database. MySQL is often the direct choice when a framework, managed host, or existing operations team already targets it. Its InnoDB storage engine provides the transactional behavior expected by conventional web applications, and its broad connector support reduces integration work. PostgreSQL gives a data model more ways to express rules through constraints, types, indexes, and extensions. It suits applications that expect detailed reporting or database-side behavior and have a team prepared to operate a PostgreSQL server. MariaDB is strongest when the surrounding stack names MariaDB support instead of assuming it from MySQL support. The shared client protocol helps applications connect, while the separate release line requires its own compatibility and upgrade checks. SQLite removes server administration from the application architecture. That makes local distribution simple, but every process that writes must coordinate through the same database file. Network-separated clients and sustained concurrent writes point toward a client-server engine. ## How Licensing Differs Licensing may affect redistribution or a product that embeds database software. Small hosted applications usually decide from workload and support first. Record the license before any database software or connector is distributed with a product. | Database | Licensing Model | | MySQL | GPL open-source edition or Oracle commercial license | | PostgreSQL | Permissive PostgreSQL License | | SQLite | Public domain | | MariaDB | GPLv2 Community Server | Read the official license terms for the exact product and connector being distributed. A database server, client library, management tool, and commercial support package can have different terms. ## Which SQL Syntax Is Portable Basic selections transfer well across these four databases. The following query works in MySQL, PostgreSQL, SQLite, and MariaDB when the table and column types match: ```sql SELECT product_name, price FROM products WHERE stock_count > 0 ORDER BY price DESC LIMIT 5; ``` It returns the five most expensive products that are in stock. Even portable-looking queries still need tests because collations, null ordering, types, and functions can change the result. Generated numeric keys show a common syntax divergence: ```sql -- MySQL and MariaDB id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY -- PostgreSQL id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY -- SQLite id INTEGER PRIMARY KEY ``` All three forms can generate a row identifier, but their rules are not identical. PostgreSQL identity columns use an implicit sequence. SQLite gives an `INTEGER PRIMARY KEY` the rowid behavior documented by SQLite. Copying a schema between engines requires more than renaming the connection string. ## Common Pitfalls ### Choosing by Popularity Instead of Workload A popularity chart does not describe the application's write concurrency, deployment target, support contract, or backup process. Choose from the workload and operating constraints, then confirm that the framework and host support the exact engine. ### Assuming SQL Is Fully Portable Tables, selections, filters, and joins share a common shape. Generated keys, upserts, JSON functions, date arithmetic, full-text search, collations, and administrative commands often differ. Run migrations and integration tests against every engine the application claims to support. ### Treating Compatibility as Identity MariaDB and MySQL share substantial history and protocol compatibility, but their features and release lines have diverged. SQLite also accepts familiar SQL while using a different typing and concurrency model. Compatibility must be proven with the real schema and queries. ## Comparison Guides - [MySQL vs PostgreSQL vs SQLite](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/) examines deployment, concurrency, JSON, search, and web application fit. - [MySQL vs MariaDB vs MaxDB](/programming/sql/comparisons/mysql-vs-mariadb-vs-maxdb/) explains the MySQL-family history and current choices. - [SQL vs NoSQL](/programming/sql/comparisons/sql-vs-nosql/) compares relational, document, key-value, graph, and wide-column data models. ## Frequently Asked Questions ### Which SQL database is best for a beginner? SQLite is the shortest route to running local SQL because it needs no separate server. PostgreSQL or MySQL is a better first client-server database when your goal is a hosted application. Learn tables, keys, joins, and transactions before engine-specific features. ### Can an application switch databases later? Yes, but the move is rarely automatic. Data types, generated keys, upserts, date functions, JSON behavior, collations, and migration tools can differ. Test the schema and queries against the destination engine, then rehearse data export, import, verification, and rollback. ### Are MySQL and MariaDB the same database? No. MariaDB began as a MySQL fork and retains substantial protocol and syntax compatibility, but the products now have separate release lines and feature differences. Check the application's support matrix and test the exact server version instead of treating the names as interchangeable. ### Does licensing matter for a typical hosted web application? For most hosted applications licensing rarely changes the technical decision; workload, support, and operations decide first. It becomes a real constraint when database software or a connector ships inside a distributed product, so check it at that point rather than as a starting filter. ### Is NoSQL one of the options this comparison covers? No. This hub compares SQL engines only: MySQL, PostgreSQL, SQLite, and MariaDB. Choosing between a relational database and a document, key-value, graph, or wide-column store is a separate decision covered in the SQL versus NoSQL comparison. ### Where does MaxDB fit into this comparison? MaxDB is covered separately because it is not a MySQL-family choice for a typical new application. It belongs to the SAP DB lineage and suits existing SAP environments. See the MySQL versus MariaDB versus MaxDB comparison for that history and its narrow use case. ## What to Read Next Use the [SQL database guides](/programming/sql/databases/) for per-engine setup and operating details. Return to the [SQL hub](/programming/sql/) for joins, schema design, indexes, transactions, imports, and other how-tos. --- ## MySQL vs MariaDB vs MaxDB URL: /programming/sql/comparisons/mysql-vs-mariadb-vs-maxdb/ Cluster: Programming > Sql Choose by the system that must run the database. MySQL fits a supported MySQL stack, MariaDB fits a stack tested for MariaDB, and MaxDB belongs in an SAP context where its documentation and support path are already part of the environment. The three names share history, and they are not interchangeable. MySQL and MariaDB still run on closely related code, while MaxDB follows a separate SAP DB line. ## Quick Comparison | Database | Lineage | Starting Context | | MySQL | MySQL AB, now Oracle | Supported MySQL stack | | MariaDB | MySQL fork | MariaDB-tested stack | | MaxDB | SAP DB | Existing SAP environment | MySQL and MariaDB share enough history to make migrations possible, but their current features and defaults differ. MaxDB shares naming history with MySQL AB, not the MySQL server code line. ## How the Three Databases Are Related Oracle's MySQL history records that the product's name honors a daughter of co-founder Monty Widenius. The product later became part of Oracle through its acquisition of Sun Microsystems. Current MySQL documentation describes a client-server relational database with InnoDB as its default storage engine. MariaDB began as a fork created by members of the original MySQL team. Its documentation still describes substantial client-protocol and SQL compatibility, while also listing growing differences between maintained MariaDB releases and MySQL. MaxDB follows a completely different code lineage. SAP's documentation states that SAP and MySQL AB signed a cooperation agreement in May 2003 and that SAP DB 7.5 would be delivered under the MaxDB name. SAP's documentation site lists a MaxDB 7.9 library and current SAP Help material. MaxDB is an SAP-focused database line with SAP DB ancestry. ## When MySQL Fits [MySQL](/programming/sql/databases/mysql/) is a sound starting point when the framework, managed provider, deployment tools, and operations team explicitly support MySQL. This often includes conventional PHP applications and services already built around MySQL connectors and backup tooling. Check the operational criteria before choosing it: - The exact MySQL release is supported by the provider and application. - InnoDB, SQL modes, collations, and authentication defaults are documented. - Backups restore successfully in a clean environment. - Slow queries can be inspected with real plans and production-shaped data. Existing support removes integration risk, but it does not excuse an untested schema or recovery path. ## When MariaDB Fits [MariaDB](/programming/sql/databases/mariadb/) fits when the platform names MariaDB support, the team operates MariaDB already, or a MariaDB-specific capability is part of the requirement. Treat it as a deliberate database choice rather than an unlabeled substitute for MySQL. MariaDB's official compatibility guide explains why version pairing matters. Older equivalent releases could behave as drop-in replacements in many cases, while later releases contain more implementation differences. JSON storage, GTIDs, authentication, collations, system variables, and optimizer behavior are all areas where a real migration can expose differences. An application that works with one MySQL-family server still needs a compatibility test against the other. ## Where MaxDB Fits MaxDB fits specialized environments built around SAP systems. An inherited system may use it as an application database, as part of an SAP installation, or behind an integration that only exposes reports and exports. Identify that role before proposing any migration. Do not infer product status from the age of the 2003 naming agreement. SAP's availability page lists MaxDB 7.9 as available through its end of maintenance on December 31, 2027, and says patches have been delivered only for 7.9.11 since June 2024. That support status does not establish market share, common web-hosting availability, or suitability for an unrelated greenfield PHP application. For an inherited MaxDB system, establish four facts first: 1. Name the application or SAP component that owns the data. 2. Identify every process that writes to the database. 3. Find the current SAP documentation and support owner. 4. Test exports, restores, and dependent integrations before changing anything. ## Test Compatibility with Real Queries A connection test proves only that a client can connect. Build a small probe from the application's important reads, including grouping, null handling, date logic, and ordering. A neutral baseline can start with a query such as this: ```sql SELECT customer_id, COUNT(*) AS order_count FROM orders WHERE status = 'paid' GROUP BY customer_id ORDER BY order_count DESC; ``` With paid orders for customers 7, 7, and 12, the expected result is customer 7 with a count of 2, followed by customer 12 with a count of 1. Run the same fixture on the source and target databases, then compare both values and ordering. Add application-specific probes for generated identifiers, JSON expressions, collations, date arithmetic, transactions, and query plans. ## Common Pitfalls and Debugging ### Assuming MySQL and MariaDB Are Identical A query may parse on both servers and still produce different plans or edge-case behavior. Record the exact versions, compare official compatibility notes, run the application's migrations, and test with representative data. Pay special attention to authentication, JSON, replication, collations, and SQL modes. ### Treating MaxDB as Another MySQL Fork This mistake sends a migration toward the wrong manuals and tools. MaxDB continues along the established SAP DB code line. Start with SAP's MaxDB documentation and the owning SAP system, then document which application boundary actually touches the database. ### Testing Only the Connection A successful login does not verify schema behavior, query results, transactions, backup formats, or restore procedures. Run a fixture-based query suite, restore a copy of representative data, and rehearse rollback before changing the production database. ## How to Decide - Choose MySQL when the application and provider document MySQL support and the team can operate its recovery path. - Choose MariaDB when the stack explicitly supports MariaDB and its release-specific behavior has been tested. - Continue with MaxDB when an SAP dependency, support path, and existing system make MaxDB the documented database. - Expand the comparison when the workload points outside this family. PostgreSQL and SQLite solve different deployment and modeling problems. No universal winner follows from the shared history. The deciding evidence is the application's support matrix, workload, migration test, and restore procedure. ## Frequently Asked Questions ### Can a MariaDB backup restore into MySQL? A logical MariaDB dump may import into MySQL when the exact schemas and features remain compatible, but the backup format is not a portability guarantee. Test the source and target versions, SQL modes, collations, data types, routines, and MariaDB-specific syntax, then verify the restored data and application queries before migration. ### Is MaxDB a version of MySQL? No, MaxDB continues the SAP DB code line. SAP and MySQL AB agreed in 2003 to deliver SAP DB 7.5 under the MaxDB name, but that naming and distribution history did not turn MaxDB into the MySQL server. ### Should a new PHP project use MaxDB? Choose MaxDB only when an existing SAP environment, support arrangement, or integration requirement makes it the documented target. For a typical new PHP application, compare supported MySQL, MariaDB, PostgreSQL, and SQLite options against the host, framework, workload, and recovery plan. ### Does choosing MariaDB over MySQL affect which PHP frameworks or ORMs will work? Most PHP frameworks and ORMs connect through the same MySQL client protocol and treat MariaDB as a supported target, so basic compatibility is common. Confirm the specific framework version documents MariaDB support and test its generated queries and migrations against the exact release. ### Is there a cost difference between running MySQL and MariaDB in production? Both are free to run under their open-source licenses; cost differences come from managed-hosting pricing, support contracts, or a commercial MySQL edition rather than the community server itself. Compare the specific hosting or support plan rather than assuming one is inherently cheaper. ## What to Read Next Use the [MySQL vs PostgreSQL vs SQLite comparison](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/) when deployment shape matters more than family history. Return to the [SQL comparison guides](/programming/sql/comparisons/) for the full decision guide, or continue with [SQL schema design basics](/programming/sql/schema-design/) before planning a migration. --- ## MySQL vs PostgreSQL vs SQLite URL: /programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/ Cluster: Programming > Sql Choose from deployment, write concurrency, data rules, provider support, and recovery needs. [MySQL and PostgreSQL](/programming/sql/comparisons/) run as a server that many clients share, while SQLite keeps the database in a file beside the application. ## Quick Comparison MySQL and PostgreSQL coordinate clients through a server process, while SQLite reads and writes a database file through the application. | Database | Deployment | Starting Fit | | MySQL | Database server | Conventional hosted apps | | PostgreSQL | Database server | Detailed relational models | | SQLite | Application file | Local or embedded data | That layout often matters more than a long feature list. ## Choose by Deployment Shape Begin by drawing where the application and database run. A desktop tool that owns one local file has different needs from a hosted service with several application instances and frequent writes. - Choose a client-server engine when many processes or machines must coordinate writes. - Consider SQLite when the data belongs to one application or device and writes can queue. - Check which engines the framework, managed provider, deployment platform, and backup tooling support directly. - Confirm who will patch, monitor, back up, and restore the database. SQLite's official guidance states that it permits many simultaneous readers and one writer at a time per database file. It recommends a client-server database when many clients need to send SQL over a network or many writers cannot take turns. ## When MySQL Fits [MySQL](/programming/sql/databases/mysql/) fits conventional web applications when the surrounding stack already targets MySQL. PHP drivers, managed services, hosting panels, migration tools, and operational experience can make that support path shorter. Use MySQL as the starting candidate when: - The provider documents MySQL as a supported service. - The application and dependencies test against the selected MySQL release. - The workload is ordinary relational CRUD with client-server concurrency. - The team has a verified MySQL backup and restore procedure. Do not carry old PHP habits into a new schema. Use InnoDB, parameterized queries, constraints, tested migrations, and current drivers. ## When PostgreSQL Fits [PostgreSQL](/programming/sql/databases/postgresql/) fits when detailed constraints, specialized types, extensions, indexing options, or database-side querying are central to the product. The PostgreSQL project describes it as an object-relational database that extends SQL for complex data workloads. Use PostgreSQL as the starting candidate when: - The schema needs detailed integrity rules and expressive data types. - Reporting, window functions, indexing, or full-text search will do substantial work. - An extension such as PostGIS is a documented requirement. - The team and provider support PostgreSQL operations and upgrades. A larger feature surface is useful only when the application needs it and the team can operate it. PostgreSQL does not repair a vague schema or an untested restore plan. ## When SQLite Fits [SQLite](/programming/sql/databases/sqlite/) fits local applications, embedded devices, command-line tools, test suites, application file formats, and some low-write websites. It removes the separate server, which reduces deployment and administration. Use SQLite as the starting candidate when: - One application or device owns the database file. - Write transactions are brief and can take turns. - Simple distribution and local access matter more than network coordination. - The backup plan can safely copy or snapshot the database. SQLite is a serious database whose main boundary is topology. A file on one device is a strong design for local data and a weak substitute for a shared database server across several machines. ## Compare Portable SQL Basic selections, filters, grouping, and ordering often transfer across all three engines. This query returns the two customers with the highest paid-order totals: ```sql SELECT customer_id, SUM(total_cents) AS paid_total FROM orders WHERE status = 'paid' GROUP BY customer_id ORDER BY paid_total DESC LIMIT 2; ``` For fixtures where customer 7 has paid orders of 1200 and 800 cents, and customer 12 has one paid order of 1500 cents, the expected rows are customer 7 with 2000 followed by customer 12 with 1500. Portable queries do not make a whole schema portable. Generated identifiers, upserts, JSON expressions, date arithmetic, collations, null ordering, full-text search, and administrative commands need engine-specific tests. ## Compare JSON and Search All three engines have JSON functions, but they store and index JSON differently. PostgreSQL offers `json` and `jsonb`, including indexes for common `jsonb` operations. MySQL provides a native JSON type and supports indexing extracted values through generated or compatible virtual columns. SQLite stores ordinary JSON as text and also supports its own internal JSONB format. SQLite's documentation warns that its JSONB is not binary-compatible with PostgreSQL JSONB and does not make PostgreSQL's lookup-performance claims. The shared name is not a portability guarantee. Search capabilities follow the same product-specific pattern. PostgreSQL has full-text search types, dictionaries, ranking, and indexes. MySQL supports `FULLTEXT` indexes and search modes for supported storage engines and text columns. SQLite provides FTS modules for search close to local file-based data. Choose by testing the application's real search query. Product search with typo tolerance, language rules, facets, and analytics may still need a dedicated search service regardless of the primary relational database. ## Common Pitfalls and Debugging ### Choosing from a Generic Benchmark A local SQLite read, a tuned PostgreSQL analytical query, and a MySQL web workload measure different systems. Reproduce the application's data size, indexes, query mix, write concurrency, and network layout. Record latency and resource use under that workload. ### Putting SQLite on a Network Filesystem A shared file does not become a client-server database. File locking and network behavior can make this design unsafe or fragile. Keep SQLite close to the application that owns the file, or move concurrent network clients to MySQL or PostgreSQL. ### Assuming Portable SQL Means Portable Schemas A simple `SELECT` can work everywhere while migrations fail on identity columns, types, constraints, or functions. Run the complete migration sequence and integration suite against every supported engine, then compare results with known fixtures. ## How to Decide The table maps common requirements to a starting point that still needs workload and recovery testing. | Requirement | Starting Point | | Hosted PHP stack | MySQL | | Detailed data rules | PostgreSQL | | Device-local storage | SQLite | | Concurrent network writes | MySQL or PostgreSQL | | PostGIS requirement | PostgreSQL | | Single-file app state | SQLite | Validate the starting point against provider support, application queries, write concurrency, migration tooling, and restore tests. A different engine wins whenever those criteria change. ## Frequently Asked Questions ### Which database is best for a small PHP app? SQLite fits a single-server or local PHP app with light write concurrency and simple file-based deployment. MySQL fits conventional PHP stacks on supported hosting. PostgreSQL fits when richer constraints, types, extensions, or reporting justify a client-server database. The hosting and recovery plan decide the starting point. ### Can SQLite handle concurrent users? SQLite can handle concurrent users and allows many simultaneous readers, but only one writer can hold the write lock at a time for each database file. Brief write transactions can usually queue successfully. Sustained concurrent writes or several application servers usually point toward MySQL or PostgreSQL. ### Is PostgreSQL always better than MySQL? The better engine depends on the workload. PostgreSQL offers a broad set of types, constraints, indexes, extensions, and database-side features. MySQL may be the better operational fit when the application, provider, and team already support it. Compare requirements and recovery procedures instead of counting features. ### Can an application switch databases later? An application can switch databases later, but the move needs planned migration work. Generated keys, data types, JSON behavior, full-text search, collations, date functions, upserts, and administrative tools differ. Test every migration and important query against the destination, then rehearse export, import, verification, and rollback. ## What to Read Next Use the [SQL database guides](/programming/sql/databases/) for engine-specific details. Compare a different data model in [SQL vs NoSQL](/programming/sql/comparisons/sql-vs-nosql/), or continue with [SQL schema design basics](/programming/sql/schema-design/) before creating production tables. --- ## SQL vs NoSQL: Relational vs Document, Key-Value, and Graph URL: /programming/sql/comparisons/sql-vs-nosql/ Cluster: Programming > Sql Start with relational SQL when orders, payments, permissions, or inventory need enforced relationships and transactions. Reach for a specific NoSQL store when one access pattern dominates: cached sessions, nested documents, network traversal, or search. SQL versus NoSQL is a choice between data models and operating trade-offs. Relational SQL keeps structured tables with enforced links, while NoSQL systems specialize in documents, keyed items, connected graphs, or partitioned records. ## What SQL and NoSQL Mean A relational SQL database stores data in tables with defined columns. Primary keys identify rows, foreign keys connect tables, constraints reject invalid values, and transactions protect related changes. SQL queries operate on sets of rows and can join facts across tables. NoSQL is an umbrella label for several database families. It includes document databases, key-value stores, graph databases, wide-column databases, search engines, and other products with different APIs and guarantees. A statement about one family does not automatically apply to another. ## Quick Comparison The table maps each data need to the model most likely to support its main access pattern. | Data Need | Starting Model | Main Trade-off | | Related business records | Relational SQL | Defined schema | | Whole nested records | Document store | Duplication risk | | Keyed temporary state | Key-value store | Narrow queries | | Connected traversal | Graph database | Specialized model | | Partitioned write paths | Wide-column store | Query-first tables | | Ranked text retrieval | Search engine | Secondary copy | The starting model still needs proof from product-specific documentation and workload tests. A document store can reference other documents, a relational database can store JSON, and a graph database can enforce constraints. ## When Relational SQL Fits Start with relational SQL when the application has stable entities and relationships that must remain valid. Common examples include customers and orders, invoices and line items, users and roles, bookings and resources, or products and inventory movements. [SQL schema design](/programming/sql/schema-design/) lets the database state those rules directly: ```sql CREATE TABLE orders ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, customer_id bigint NOT NULL REFERENCES customers (id), status text NOT NULL CHECK (status IN ('pending', 'paid', 'cancelled')) ); ``` An order cannot reference a missing customer, and an unsupported status is rejected. The same rules apply whether a web route, import job, or administrator attempts the write. Relational SQL also fits workloads that need flexible reporting. The same normalized records can support customer views, finance reports, inventory checks, and administrative queries through joins and aggregation. ## When a NoSQL Model Fits Start with a specific NoSQL model when its access pattern is clearer than the relational alternative. The requirement should name both the data shape and the dominant operation. - Read one nested content record as a whole. - Fetch or expire a value by key. - Traverse several meaningful relationship hops. - Write data across known partitions at high volume. - Rank and facet tokenized text. Each statement points to a different product family. "Use NoSQL for scale" is too vague to design or test because the word does not specify partitioning, queries, consistency, or ownership. ## How Document, Key-value, Graph, and Wide-column Stores Differ Document databases store nested records that can be retrieved together. MongoDB's official modeling guidance distinguishes embedding from references. Embedding can reduce reads when related data belongs together, while references suit independently queried records, large hierarchies, or complex many-to-many relationships. Key-value stores center the lookup on a key. Redis provides strings, hashes, lists, sets, sorted sets, streams, JSON, and other types with different operations. This can fit caches, counters, queues, rate limits, and temporary sessions when the key is the natural access path. Graph databases make connections explicit in their stored model. Neo4j's property graph uses nodes, relationships, labels, and properties. It fits queries that follow connected paths, such as dependency impact, fraud rings, routing, or authorization relationships with meaningful edges. Wide-column databases design partitions and tables around known queries. Apache Cassandra distributes data across nodes and documents its architecture in terms of partitioning, replication, storage, and guarantees. This is a different design process from creating normalized tables and adding several general-purpose query paths later. ## Consistency and Partition Trade-offs Do not map "SQL" to consistent and "NoSQL" to inconsistent. Database products expose different transaction scopes, isolation levels, replication modes, quorum settings, and failure behavior. For a distributed system, define the user-visible rule before selecting a setting: - Must a completed write appear in the next read? - Can a cache or feed show stale data briefly? - Can two regions accept changes to the same fact? - What happens when nodes cannot communicate? - Which process detects and repairs conflicts? Payment capture, permissions, and inventory reservations usually need stricter coordination than analytics events or a rebuildable cache. The business rule sets the required guarantee. ## The JSON Middle Ground Relational and document features can coexist inside one database. PostgreSQL's `jsonb` type stores a decomposed binary representation and supports operators plus GIN indexing for common containment and key queries. ```sql CREATE TABLE product_events ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, product_id bigint NOT NULL REFERENCES products (id), event_type text NOT NULL, payload jsonb NOT NULL ); CREATE INDEX product_events_payload_idx ON product_events USING GIN (payload); ``` The stable relationship stays in `product_id`, while variable event details stay in `payload`. The foreign key protects the product relationship, and the GIN index can support documented JSONB queries. JSON does not remove the need for a model. Put stable fields in typed columns when they need constraints, joins, or common indexes. Use JSON for payloads and attributes whose variability is part of the requirement. ## Using More than One Data Store An application can use more than one store when each has a named job. A relational database might own customers, orders, payments, and permissions. Redis can hold expiring cache entries for the application. A search engine might receive product documents for ranking and facets. Mark one system as the owner of each business fact. Secondary stores should receive projections or copies through a documented process. If two stores can independently change the same order status, the architecture also needs conflict resolution, retries, reconciliation, and failure repair. A mixed design adds operations and debugging paths. Add a second store only when its access pattern earns that cost. ## Common Pitfalls and Debugging ### Choosing NoSQL to Avoid Schema Design Flexible records still develop a schema through field names, value types, validators, indexes, partition keys, and application assumptions. Write sample documents and queries first, then specify which variations are allowed. Unplanned shape drift is a data-quality bug. ### Treating NoSQL as One Consistency Model Read the exact product's transaction and consistency documentation. Test failure cases with the selected configuration, including stale reads, retries, duplicate writes, and unavailable nodes. Database category labels cannot replace product-specific failure tests. ### Letting Two Stores Own the Same Fact Conflicting values appear when two stores accept independent updates. Choose a canonical owner, make other copies derived, and record how lag and rebuilds work. Add reconciliation checks for facts that affect money, access, or inventory. ## How to Decide Choose the smallest set of stores that can answer the application's real questions without creating competing owners for the same fact. 1. Name the canonical business facts and required relationships. 2. List the dominant reads, writes, traversals, and reports. 3. State transaction, consistency, and stale-read requirements. 4. Draw the deployment, partition, and failure boundaries. 5. Choose the smallest set of stores that meets those requirements. 6. Test with representative data and expected failure cases. Start with relational SQL when relationships, constraints, transactions, and varied reporting dominate. Start with a named NoSQL family when a specific document, key, graph, partition, or search pattern dominates. Use both only when ownership remains clear. ## Frequently Asked Questions ### Is NoSQL faster than SQL? Neither database category is universally faster across workloads. Performance depends on the database family, data model, indexes, query pattern, network layout, consistency settings, and workload. Compare representative reads and writes on production-shaped data instead of treating SQL or NoSQL as a benchmark result. ### How do you evolve a NoSQL document schema? Evolve a document schema with explicit version fields or recognizable shapes, validators for new writes, readers that handle supported older records, and a measured backfill plan. Update indexes and queries with the document change, then remove compatibility code only after stored records and dependent services have migrated. ### Can PostgreSQL store document data? PostgreSQL can store document data in JSON and JSONB columns. JSONB supports operators and indexes for common document queries. Keep stable identifiers and relationships in typed columns when they need constraints, then use JSONB for genuinely variable attributes or payloads. ### Can one application use SQL and NoSQL? One application can use SQL and NoSQL together. A relational database can own canonical accounts, orders, and permissions while a key-value store handles cache or a search engine serves an index. Define one owner for each business fact and make every secondary copy rebuildable or reconcilable. ## Self-Check 1. An order must reference an existing customer. Which model provides a direct database constraint? A. Relational SQL. B. A key-value store. C. A search engine. Answer: A. A relational foreign key can reject an order whose customer row does not exist. 2. A cache entry is fetched only by session ID and expires after an hour. Which starting model fits? A. A graph database. B. A key-value store. C. A wide-column store. Answer: B. The lookup and lifecycle are both centered on one key. 3. Does a document database remove schema work? No. The schema moves into document shape, validation, indexes, query code, and migration rules. 4. Why should a search index avoid owning product prices? Search data is usually a derived copy. The canonical store needs to own the price so lag or index rebuilds cannot create competing truths. 5. When does PostgreSQL JSONB provide a middle ground? It fits variable document attributes that still belong beside relational identifiers, constraints, transactions, and ordinary SQL queries. ## What to Read Next Continue with [SQL schema design basics](/programming/sql/schema-design/) to model relational facts, then use [SQL transactions and ACID](/programming/sql/transactions/acid/) for related writes. Return to the [SQL comparison guides](/programming/sql/comparisons/) when the decision is between relational engines. --- ## SQL Databases URL: /programming/sql/databases/ Cluster: Programming > Sql These pages cover the database engines that speak SQL. They share most of the language but differ in setup, scale, and where they fit best. If you are weighing them against each other, the [comparisons](/programming/sql/comparisons/) put them head to head. ## Where Each Engine Actually Fits MySQL became the default database for the LAMP stack, the well-known pairing of Linux, Apache, MySQL, and PHP that ran a huge share of the early web on a single rented server. That history left it with an enormous ecosystem: managed hosting nearly everywhere you look, and drivers for every programming language still in common use. Oracle now owns the trademark and steers development, offering MySQL under a dual license, open source under the GPL alongside a separate commercial license for vendors who need one. It still handles ordinary web traffic well, and the old gap with PostgreSQL on complex queries has narrowed considerably over the years. PostgreSQL grew out of an academic project at Berkeley aimed at extending SQL with richer data types and stronger constraints, and that lineage still shows in how seriously it takes standards compliance. It handles complex queries and unusual data types better than most alternatives, and extensions add capabilities on top of that, things like geographic data or full text search. Its reputation for being harder to set up than MySQL is mostly outdated at this point, and its replication and backup tooling has matured into something teams run in production. SQLite works nothing like a typical server-based database. The entire engine is a library that your application links against directly, and the whole database lives in a single file on disk, with no separate server process or network protocol to manage. That simplicity makes it reliable for local tools, desktop apps, mobile apps, and embedded devices, and it's tested well enough that it quietly runs inside an enormous number of everyday products. It struggles the moment a busy multi-user web backend needs several processes writing to the same database at once, because SQLite still assumes one writer at a time. MariaDB exists because MySQL's original developers forked the project after Oracle acquired the company that owned it, worried about who would control its future direction. The fork kept the same wire protocol and most of the same SQL syntax, so it still works as a close drop-in replacement for a large share of MySQL workloads. It remains fully community-governed rather than steered by a single vendor, which is the reason some teams pick it over MySQL even though the two behave almost identically day to day. ## Picking Among Them Start with PostgreSQL for a new server-side application unless you already have a specific reason not to. Its standards compliance and query flexibility give you more room to grow into complex reporting or analytics later without hitting a wall. Stick with MySQL or MariaDB when your team or hosting setup already has deep experience with one of them, since familiarity and existing tooling usually beat a marginal technical edge on paper. Reach for SQLite the moment the job is local rather than networked: a desktop app, a mobile app, a command-line tool, or a prototype you want to keep in one portable file. Choose MariaDB specifically over MySQL when open governance matters to your organization or when you're moving away from MySQL over licensing concerns, since it gives you nearly the same behavior without a single company controlling the roadmap. ## Databases - [MySQL](/programming/sql/databases/mysql/): the long-time default for web apps, widely hosted and documented. - [PostgreSQL](/programming/sql/databases/postgresql/): the feature-rich, standards-leaning choice for serious data work. - [SQLite](/programming/sql/databases/sqlite/): a whole database in a single file, perfect for local and embedded use. - [MariaDB](/programming/sql/databases/mariadb/): the community fork of MySQL, drop-in for most projects. ## What to Read Next - [SQL Comparisons](/programming/sql/comparisons/): which engine to pick, and SQL vs NoSQL. - [SQL Guides](/programming/sql/): the concepts that apply across all of them. ## Frequently Asked Questions ### Do MySQL and MariaDB read each other's data? Largely yes for ordinary tables, since MariaDB forked from MySQL and kept close file and protocol compatibility. Features added to either since the fork are not guaranteed to carry across, so treat compatibility as historical rather than ongoing. ### Is PostgreSQL slower than MySQL for simple lookups? Not meaningfully, for ordinary indexed queries on either. The differences that mattered years ago have narrowed, and a real performance question is answered by measuring the actual query rather than by an engine's reputation. ### Does SQLite need a server process? No, which is what separates it from the others. SQLite is a library the application links against, with the whole database in one file, no network protocol, and nothing to start or administer. ### Can one application use several database engines at once? Yes, at a real cost: separate connections, separate migration tooling, and no transactions spanning them. It is a deliberate architectural choice, such as SQLite for local caching alongside a networked primary. --- ## MariaDB: MySQL Compatibility, Engines, and Config Files URL: /programming/sql/databases/mariadb/ Cluster: Programming > Sql MariaDB is an open-source relational database in the MySQL family. It speaks the MySQL client protocol and supports familiar SQL, while maintaining its own releases, features, storage engines, and compatibility rules. Pick MariaDB when your framework, host, or Linux distribution names MariaDB support, and test the exact release your application will run. MariaDB and MySQL share an early history, but each now has features the other lacks. ## How MariaDB Began MariaDB development began in 2009 as a fork of MySQL led by MySQL co-founder Michael "Monty" Widenius. Oracle announced its agreement to acquire Sun Microsystems in April 2009, and the acquisition was completed in January 2010. Sun owned MySQL at the time, so the fork preserved a separately governed MySQL-family database during that ownership change. The first MariaDB Server release arrived in October 2009. MariaDB continued the naming pattern recorded in the official histories: MySQL was named after Widenius's daughter My, and MariaDB was named after his daughter Maria. The shared history explains the similar client protocol, SQL syntax, tools, and terminology without guaranteeing permanent equivalence. MariaDB and MySQL now publish separate release lines and implement features independently. ## Where MariaDB Fits MariaDB fits applications that already use MySQL-family conventions and have explicit support from the framework, host, Linux distribution, or managed provider. Ordinary tables, joins, indexes, InnoDB transactions, and common connectors can make the first setup look very similar to MySQL. Choose MariaDB when the surrounding application and operations support these conditions: - The application officially supports the intended MariaDB release. - The host or operating system maintains MariaDB packages and upgrades. - The team already understands MySQL-family administration and SQL. - MariaDB-specific features or storage engines solve a documented requirement. - Backup, restore, monitoring, and replication tools have been tested with MariaDB. Do not choose it only because a dependency says "MySQL compatible." Compatibility depends on the exact server, connector, SQL mode, collation, feature set, and migration path. ## How to Install, Connect, and Check the Server Follow MariaDB's platform-specific installation instructions for the target environment. Package names and option-file locations vary across Linux distributions, containers, macOS, Windows, and managed services. On a Debian or Ubuntu development machine, the package-manager path commonly uses these commands: ```bash sudo apt update sudo apt install mariadb-server mariadb-client sudo systemctl status mariadb ``` After installation, use the install-time administrative connection and ask the server for its identity: ```sql sudo mariadb SELECT VERSION(); SHOW VARIABLES LIKE 'version_comment'; SHOW VARIABLES LIKE 'default_storage_engine'; ``` The output confirms the server release, distribution label, and default storage engine. Record the full version in deployment notes instead of writing only "MySQL/MariaDB." ## How to Create an Application User MariaDB accounts combine a user name with a host. The account `'app_runtime'@'127.0.0.1'` is different from `'app_runtime'@'%'`. Use the narrowest host rule that matches the deployment. Create a runtime user for one application database: ```sql CREATE DATABASE codewalkers_app CHARACTER SET utf8mb4; CREATE USER 'app_runtime'@'127.0.0.1' IDENTIFIED BY 'replace-with-a-generated-secret'; GRANT SELECT, INSERT, UPDATE, DELETE ON codewalkers_app.* TO 'app_runtime'@'127.0.0.1'; SHOW GRANTS FOR 'app_runtime'@'127.0.0.1'; ``` Leave the administrative session, then verify the restricted account through the same host used by the PHP connection: ```bash mariadb -u app_runtime -p -h 127.0.0.1 codewalkers_app ``` PHP uses the PDO MySQL driver for MariaDB connections because MariaDB supports the MySQL client protocol. Use the same restricted account in the application: ```php $pdo = new PDO( 'mysql:host=127.0.0.1;dbname=codewalkers_app;charset=utf8mb4', 'app_runtime', $password, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ], ); ``` The runtime role can read and change application rows, while schema changes remain outside its grants. Run migrations through a separate account with the required DDL (schema-change) privileges, then remove or disable that access when the deployment process allows it. Use secret storage provided by the deployment platform and rotate a credential if it appears in a log, terminal history, or committed file. Test the restricted account before deployment because an application that silently depends on root privileges has an incomplete permission model. ## How System-Versioned Tables Work MariaDB introduced system-versioned tables in its 10.3.4 release. A system-versioned table keeps earlier row versions, allowing a query to inspect data as it existed at another time. This can support auditing and change analysis when the storage and retention costs are planned. Create a small price table with system versioning enabled: ```sql CREATE TABLE product_prices ( product_id BIGINT UNSIGNED NOT NULL, price DECIMAL(10, 2) NOT NULL, PRIMARY KEY (product_id) ) WITH SYSTEM VERSIONING; INSERT INTO product_prices (product_id, price) VALUES (101, 19.95); UPDATE product_prices SET price = 21.50 WHERE product_id = 101; ``` A normal selection returns the current price: ```sql SELECT product_id, price FROM product_prices WHERE product_id = 101; -- product_id | price -- 101 | 21.50 ``` Use `FOR SYSTEM_TIME ALL` to include the stored history: ```sql SELECT product_id, price FROM product_prices FOR SYSTEM_TIME ALL WHERE product_id = 101 ORDER BY ROW_START; -- product_id | price -- 101 | 19.95 -- 101 | 21.50 ``` The exact period timestamps depend on when the statements run. Historical rows consume storage, and retention must be designed before a busy table keeps every change indefinitely. Verify version support and backup behavior before using this feature in production. ## How to Configure MariaDB for an Application MariaDB reads server and client options from files such as `my.cnf` or `my.ini`. The search order depends on the installation. Use the installed server to print its option-file locations instead of guessing: ```bash mariadbd --help --verbose ``` Keep the initial configuration small and documented: ```ini [mariadb] default_storage_engine=InnoDB character-set-server=utf8mb4 [client] default-character-set=utf8mb4 ``` InnoDB is the normal storage engine for transactional application tables. It supports transactions, row-level locking, crash recovery, and foreign keys. Other MariaDB storage engines target different workloads, so each non-default choice needs an operational reason. Production also needs backups, restore tests, monitoring, TLS where connections cross a network, known SQL modes, matching character sets, and a supported upgrade path. A local connection only proves the query syntax. ## How to Back Up and Upgrade MariaDB A backup is useful after it has been restored and checked. For a small InnoDB database, `mariadb-dump` can create a logical backup that is readable and suitable for a rehearsed restore: ```bash sudo mariadb-dump \ --single-transaction \ --routines \ --events \ --dump-history \ --databases codewalkers_app > codewalkers_app.sql sudo mariadb < codewalkers_app.sql ``` `--single-transaction` provides a consistent logical snapshot for transactional InnoDB tables without locking them for the full dump. `--dump-history` includes system-versioned table history and requires MariaDB 10.11 or later. The `--databases` form writes the database creation and selection statements needed by the straight restore. Check the documentation before applying the same command to non-transactional tables or a database with special backup requirements. Verify a restored backup by checking row counts, constraints, routines, events, application queries, and representative file or object references. Store backups away from the server they protect and test the recovery time against the application's needs. For an upgrade, read every release note between the source and destination lines. Run the upgrade on a restored copy, execute migrations and integration tests, compare important query plans, then rehearse rollback. Connector support and managed-host support must cover the destination release before production changes. Keep the exact server version beside each backup. Recovery after a failed or interrupted upgrade may require the previous major release to start and repair the data directory before another upgrade attempt. ## How MariaDB Differs from MySQL MariaDB and MySQL share common SQL and protocol behavior, but the branches have diverged as their implementation differences have grown. Current migrations can encounter different authentication plugins, collations, JSON behavior, replication features, optimizer decisions, system tables, release policies, and on-disk formats. Before moving an application between them, prepare a tested migration plan: 1. List every server-specific data type, function, SQL mode, and storage engine. 2. Check the connector and framework support matrix for the destination release. 3. Run the full migration and test suite against a copy of production-shaped data. 4. Export and import through documented tools where direct file compatibility is unsupported. 5. Restore a backup on the destination release and verify row counts and constraints. A shared port and driver make connection setup easier. Migration testing still proves whether the application works. ## Common Pitfalls ### Assuming Drop-In Means Identical Cause: old compatibility guidance is applied to a newer MariaDB and MySQL pair. Fix: compare the exact versions, read the compatibility notes, and run migrations plus integration tests on MariaDB. ### Running the Application as Root Cause: local setup credentials are copied into the deployed application. Fix: create a limited runtime user, keep schema-change permissions in a separate migration role, and rotate exposed credentials. ### Copying Unknown Tuning Settings Cause: a large configuration from another workload is treated as a universal baseline. Fix: keep defaults unless a measured workload justifies a change, then document the reason and test the result. ### Ignoring History Growth Cause: system versioning is enabled without retention or storage planning. Fix: estimate change volume, define how long history is required, monitor table growth, and test backup and restore times. ## Frequently Asked Questions ### Is MariaDB a drop-in replacement for MySQL? Drop-in compatibility is specific to the MySQL version and application. MariaDB retains substantial MySQL protocol and syntax compatibility, but features, defaults, authentication, collations, replication, and on-disk formats have diverged. Check the support matrix and test migrations, queries, backups, and restores against the exact MariaDB release. ### Why does PHP PDO use a mysql DSN for MariaDB? PHP's PDO MySQL driver speaks the MySQL client protocol used by MariaDB, so the DSN begins with `mysql`. That shared driver does not make every server feature identical. Keep the server version explicit and test the application's SQL against MariaDB. ### Which storage engine should a MariaDB web app use? InnoDB is the normal starting point for transactional web applications. It supports transactions, row-level locking, crash recovery, and foreign keys. Choose another engine only for a documented workload requirement, then test its durability, backup, locking, and operational behavior. ### Should a MariaDB application use utf8mb4 or the plain utf8 character set? Use utf8mb4. The historical utf8 character set in the MySQL family stores only a 3-byte subset of Unicode and cannot hold emoji or other characters outside that range. utf8mb4 stores the full range, and it is what this page's own schema and connection examples use. ### Does system versioning replace the need for regular backups? No. System versioning keeps row history inside the same live database and does not protect against a dropped table, a failed disk, or a corrupted data directory. Treat it as a query feature for auditing, kept alongside a tested backup and restore process. ## What to Read Next Read the [MySQL guide](/programming/sql/databases/mysql/) for the other branch of the MySQL family, then use [MySQL vs MariaDB vs MaxDB](/programming/sql/comparisons/mysql-vs-mariadb-vs-maxdb/) for a direct comparison. Continue with [SQL transactions and ACID](/programming/sql/transactions/acid/) and [PHP PDO](/programming/php/mysql/pdo/) before connecting an application. Return to the [SQL databases hub](/programming/sql/databases/) to compare MariaDB with PostgreSQL and SQLite. --- ## MySQL: Restricted Users, InnoDB, Backups, and Major Upgrades URL: /programming/sql/databases/mysql/ Cluster: Programming > Sql MySQL is a client/server relational database used for websites, business applications, and other systems that need several clients to share structured data. Choose MySQL when your framework, host, and team already support it. The setup decision that matters most is access. Give each application its own restricted account and keep administrative credentials away from ordinary requests. ## How MySQL Began The MySQL project began its public history during 1995. Its official history explains that the original developers first tried to connect their own ISAM routines through mSQL, then built a new SQL interface when mSQL did not meet their speed and flexibility requirements. The similar API made existing mSQL code easier to port. The project was named after one of co-founder Michael "Monty" Widenius's daughters. MySQL later became closely associated with the PHP-based web stack because widely available hosting put a server database within reach of small website projects. That history explains the large body of PHP examples and the continuing link with [MariaDB](/programming/sql/databases/mariadb/). It does not make old setup instructions current. MySQL now publishes separate Long-Term Support and Innovation tracks, so every installation needs an explicit release choice. ## Where MySQL Fits MySQL fits a conventional server application with relational data, concurrent clients, established MySQL tooling, and hosting that supports the selected release. Content systems, shops, internal tools, and reporting applications commonly match that shape. Choose it when the surrounding system supports these conditions: - The framework and connector support the intended MySQL release. - The data belongs in tables with keys, constraints, joins, and transactions. - The deployment provides tested backups, monitoring, and an upgrade path. - The team understands MySQL permissions, SQL modes, and InnoDB behavior. - Existing applications or operational tools already depend on MySQL compatibility. Use [PostgreSQL](/programming/sql/databases/postgresql/) when its types, constraints, extensions, or indexing options better match the data. Use [SQLite](/programming/sql/databases/sqlite/) when an embedded, single-file database removes an unnecessary server. Choose from the actual workload and deployment conditions. ## How to Install, Connect, and Check the Server Follow the official installation instructions for the target operating system or managed service. Package names, repository tracks, authentication defaults, and configuration paths vary. The worked examples below target MySQL 9.7, the current LTS release on July 29, 2026. Oracle moved to calendar versioning after the 9.7 LTS series, so the next documented line jumps to 26.7; MySQL 26.7 is the first calendar-versioned release and was still marked Early Access. Check the server instead of copying this note into a future deployment. ```bash mysql --version mysql -u root -p ``` Ask the connected server for the details that affect the application: ```sql SELECT VERSION(); SHOW VARIABLES LIKE 'version_comment'; SHOW VARIABLES LIKE 'default_storage_engine'; SHOW VARIABLES LIKE 'sql_mode'; SHOW VARIABLES LIKE 'character_set_server'; ``` This check identifies the server release, distribution, storage-engine default, SQL mode, and character set. Record the output with deployment notes because a client binary version does not prove which remote server accepted the connection. ## How to Create an Application User Give the application an account for its own database while administrators keep schema and server commands. ```sql CREATE DATABASE codewalkers_app CHARACTER SET utf8mb4; CREATE USER 'app_runtime'@'127.0.0.1' IDENTIFIED BY 'replace-with-a-generated-secret'; GRANT SELECT, INSERT, UPDATE, DELETE ON codewalkers_app.* TO 'app_runtime'@'127.0.0.1'; SHOW GRANTS FOR 'app_runtime'@'127.0.0.1'; ``` MySQL accounts include both a user name and a host. The account above is different from `'app_runtime'@'%'`, which accepts a much broader connection origin. Match the host rule to the deployment and keep schema-changing privileges in a separate migration account. Test the runtime identity through the same network path used by the application: ```bash mysql -u app_runtime -p -h 127.0.0.1 codewalkers_app ``` Store the generated password in the deployment platform's secret store. Rotate it if it appears in a committed file, log, screenshot, or terminal transcript. ## How InnoDB Transactions Work InnoDB is MySQL's default general-purpose storage engine in the current documentation. It supports transactions, row-level locking, crash recovery, and foreign-key checks. Create two related tables for a small order workflow: ```sql CREATE TABLE orders ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, customer_email VARCHAR(255) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'pending' ) ENGINE=InnoDB; CREATE TABLE order_items ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, order_id BIGINT UNSIGNED NOT NULL, sku VARCHAR(80) NOT NULL, quantity INT UNSIGNED NOT NULL, CONSTRAINT order_items_order_fk FOREIGN KEY (order_id) REFERENCES orders (id) ) ENGINE=InnoDB; ``` The next transaction inserts the order and its first item as one unit: ```sql START TRANSACTION; INSERT INTO orders (customer_email) VALUES ('reader@example.com'); SET @order_id = LAST_INSERT_ID(); INSERT INTO order_items (order_id, sku, quantity) VALUES (@order_id, 'SQL-GUIDE', 1); COMMIT; ``` A failed statement does not undo the transaction by itself. When the item insert fails, the client issues `ROLLBACK` instead of `COMMIT`, and that removes the pending order as well. Application code must still begin, commit, and roll back the correct unit of work. Read [SQL transactions and ACID](/programming/sql/transactions/acid/) before combining database changes with files, email, queues, or payment calls that cannot join the same transaction. ## How to Configure MySQL for an Application MySQL option files provide server and client startup settings. Find the files used by the installed distribution instead of assuming a path from another operating system. Keep the first configuration small and visible: ```ini [mysqld] default_storage_engine=InnoDB character_set_server=utf8mb4 collation_server=utf8mb4_0900_ai_ci sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION [client] default-character-set=utf8mb4 ``` Confirm each value after restarting the server. Strict SQL mode turns several silent data conversions into errors, which exposes invalid writes earlier. Character set and collation must also match application requirements before existing data makes a later conversion expensive. Connection limits, InnoDB memory, durability settings, binary logging, and replica settings depend on measured workload and recovery requirements. Copying a large configuration from another server can consume memory or change durability without solving the local bottleneck. ## How to Back Up and Upgrade MySQL MySQL documents logical and physical backup methods. Logical dumps are portable and readable but can be slower and larger. Physical backups can restore large databases faster but depend more closely on server files, versions, and tooling. A small InnoDB database can begin with a logical dump and a rehearsed restore: ```bash mysqldump \ --single-transaction \ --routines \ --events \ --databases codewalkers_app > codewalkers_app.sql mysql < codewalkers_app.sql ``` The restore target and credentials still need to be chosen for the actual environment. `--single-transaction` creates a consistent snapshot for transactional InnoDB tables without locking them for the full dump. It does not give the same guarantee to non-transactional tables. Verify restored row counts, constraints, routines, events, and representative application queries. Before an upgrade, identify whether the source and destination are LTS or Innovation releases. Follow MySQL's supported upgrade path, read every intervening release note, test on a restored copy, compare important query plans, and rehearse rollback. A replica can help availability during some upgrades, but it remains another copy of current changes rather than a historical backup. ## Common Pitfalls ### Copying an Old Version Number Symptom: installation fails or the managed service does not offer the tutorial's version. Cause: a stale article was treated as release authority. Fix: check MySQL's current release documentation, choose the intended LTS or Innovation track, and confirm connector plus framework support. ### Running the Application as Root Symptom: a compromised request can alter schemas or read unrelated databases. Cause: administrative credentials were reused by the runtime application. Fix: create a host-scoped runtime user with only required data privileges and reserve schema changes for a separate migration account. ### Forgetting the Transaction Boundary Symptom: an order exists without its items after the second statement fails. Cause: related writes ran under separate autocommit transactions. Fix: start one transaction around the complete database write set, commit after every statement succeeds, and roll back on failure. ### Treating Replication as Backup Symptom: an accidental delete appears on every replica. Cause: replication was expected to preserve an earlier state. Fix: keep independent backups, enable point-in-time recovery when required, and test a restore without relying on the live replication topology. ## Frequently Asked Questions ### Should a new application use MySQL LTS or Innovation? Choose an LTS release when the application needs a stable feature set and a longer support window. Choose Innovation only when the team needs its newer features and can test more frequent behavior changes. Confirm the exact server, connector, framework, and managed-service support before deployment. ### Is InnoDB the default MySQL storage engine? InnoDB is the default storage engine in current MySQL releases unless the server configuration changes that default. It supports transactions, row-level locking, crash recovery, and foreign keys. Check the running server with SHOW VARIABLES rather than assuming every installation kept the default. ### Does PHP PDO make MySQL queries safe? PDO provides prepared statements and parameter binding, but the application must use them correctly. Bind untrusted values instead of concatenating them into SQL, validate identifiers through fixed allowlists, use a restricted database account, and escape output separately when database text enters HTML. ### How do binary logs support point-in-time recovery? Binary logs record database changes after a full backup. A point-in-time recovery can restore that backup, replay the required log events, and stop before a damaging statement or at a chosen time. Retain and protect the logs for the recovery window, record their coordinates, and rehearse the complete restore sequence. ## Self-Check 1. Which account should handle ordinary application requests? A. The root account. B. A restricted runtime account. C. The schema owner. Answer: B. The runtime account needs only the data privileges used by the application, while administrative and migration privileges stay separate. 2. What happens after this sequence? ```sql START TRANSACTION; INSERT INTO orders (customer_email) VALUES ('reader@example.com'); ROLLBACK; SELECT COUNT(*) FROM orders WHERE customer_email = 'reader@example.com'; ``` Answer: The count is zero, assuming no matching row existed before the transaction. `ROLLBACK` removes the uncommitted insert. 3. Which command proves the running server version? A. `mysql --version`. B. `SELECT VERSION()`. C. `SHOW GRANTS`. Answer: B. The client command reports the local client version, while `SELECT VERSION()` asks the connected server. 4. Why does replication not replace backups? Answer: Replication normally copies destructive changes as faithfully as valid ones. Backups and point-in-time recovery preserve states that can be restored after an accidental change reaches the replicas. 5. What should happen if the second statement in a related write fails? A. Commit the first statement. B. Ignore the error. C. Roll back the transaction. Answer: C. Rolling back prevents a partial database state when the statements belong to one unit of work. ## What to Read Next Continue with [PHP PDO](/programming/php/mysql/pdo/) to connect a PHP application through bound parameters. Use [SQL indexes and query optimization](/programming/sql/indexes/query-optimization/) before adding indexes by habit, then compare [MySQL vs PostgreSQL vs SQLite](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/) when the database choice is still open. Return to the [SQL databases hub](/programming/sql/databases/) to review the full database family. --- ## PostgreSQL: Roles, JSONB, Indexes, Backups, and Upgrades URL: /programming/sql/databases/postgresql/ Cluster: Programming > Sql PostgreSQL is an open-source object-relational database that combines SQL with a broad set of types, constraints, indexes, and extensions. Choose PostgreSQL when the database itself should enforce your business rules through constraints, types, and indexes, and when your team can run its backups and major upgrades. ## How PostgreSQL Began The Berkeley POSTGRES implementation began in 1986 under Professor Michael Stonebraker. The first demonstration system operated in 1987, and Version 1 reached a small group of external users in June 1989. Andrew Yu and Jolly Chen added an SQL interpreter in 1994, and the open-source descendant was released as Postgres95. The project adopted the PostgreSQL name in 1996 to connect the original POSTGRES work with its SQL capability. The shorter name "Postgres" remains an official alternative. The long history matters because current PostgreSQL combines a mature relational core with features added over many release cycles. It does not remove the need to choose a supported version, test extensions, or plan major upgrades. ## Where PostgreSQL Fits PostgreSQL fits applications whose database must express more than basic row storage. It is a strong candidate when the data model relies on detailed constraints, several index types, JSONB alongside relational columns, geospatial work through extensions, or database-side functions and types. Choose it when these conditions match the application: - Relationships and business rules need database constraints. - Queries benefit from partial, expression, GIN, GiST, or BRIN indexes. - Extensions such as PostGIS solve a documented database requirement. - The team can operate backups, restores, monitoring, and major upgrades. - The hosting platform supports the required major version and extensions. Use [MySQL](/programming/sql/databases/mysql/) when its hosting, compatibility, and team experience are a better fit. Use [SQLite](/programming/sql/databases/sqlite/) when the application needs a local database file instead of a shared server. Choose PostgreSQL for matching data and operational requirements. ## How to Install, Connect, and Check the Server Follow PostgreSQL's platform or provider instructions because packages, service names, authentication rules, and data-directory paths differ. Connect with `psql` after installation: ```bash psql -h 127.0.0.1 -U postgres -d postgres ``` The examples below target PostgreSQL 18.4, the current stable documentation release on July 29, 2026. PostgreSQL 19 Beta 2 remained a development release on that date. Ask the server for its identity and current connection context: ```sql SELECT version(); SELECT current_database(), current_user, current_schema(); SHOW search_path; SHOW server_encoding; ``` The result identifies the server, database, role, active schema, name-resolution path, and encoding. Keep these details with deployment evidence because a local `psql` version does not prove the remote server version. ## How to Create Roles and a Schema PostgreSQL represents identity and privileges with roles. Some roles can log in, while others collect permissions or own objects. Separate ownership from the account used by ordinary application requests. ```sql CREATE ROLE app_owner NOLOGIN; CREATE ROLE app_runtime LOGIN PASSWORD 'replace-with-a-generated-secret'; CREATE SCHEMA app AUTHORIZATION app_owner; GRANT USAGE ON SCHEMA app TO app_runtime; ``` The runtime role can use the application schema without owning it. Migrations can run through an owner role or a controlled deployment identity, while web requests receive only the permissions needed for their queries. Create objects with schema-qualified names so their ownership is visible: ```sql CREATE TABLE app.users ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, email TEXT NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ALTER TABLE app.users OWNER TO app_owner; GRANT SELECT, INSERT, UPDATE, DELETE ON app.users TO app_runtime; GRANT USAGE, SELECT ON SEQUENCE app.users_id_seq TO app_runtime; ``` The explicit sequence grant lets runtime inserts obtain identity values. Default privileges need separate planning for tables and sequences created by future migrations. Test the complete migration with the migration identity, then test runtime queries again as `app_runtime`. ## How JSONB and Indexes Work Together PostgreSQL stores `json` as input text and `jsonb` in a decomposed binary form that can be processed and indexed. JSONB works well for fields whose shape varies, while stable application facts remain ordinary columns. This event table keeps ownership and event type relational: ```sql CREATE TABLE app.events ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES app.users (id), event_type TEXT NOT NULL, payload JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); INSERT INTO app.events (user_id, event_type, payload) VALUES ( 42, 'article_viewed', '{"article_id": 17, "referrer": "newsletter"}' ); ``` The payload holds event-specific details, while the user, event type, and timestamp remain visible to constraints and common reports. Add an index only after the query shape is known: ```sql CREATE INDEX events_article_id_idx ON app.events ((payload ->> 'article_id')) WHERE event_type = 'article_viewed'; EXPLAIN SELECT id, created_at FROM app.events WHERE event_type = 'article_viewed' AND payload ->> 'article_id' = '17'; ``` This partial expression index supports one repeated lookup without indexing every key in every payload. Read [SQL indexes and query optimization](/programming/sql/indexes/query-optimization/) before adding indexes that have no measured query, row count, or write-cost review. ## How to Back Up and Upgrade PostgreSQL A logical backup can capture one database in PostgreSQL's custom archive format: ```bash pg_dump \ --format=custom \ --file=codewalkers_app.dump \ codewalkers_app createdb codewalkers_app_restore pg_restore \ --dbname=codewalkers_app_restore \ codewalkers_app.dump ``` A complete recovery plan may also need roles, tablespaces, configuration, large objects, and cluster-wide settings that a single-database dump does not cover. Verify restored row counts, constraints, extensions, permissions, and application queries. Keep the backup away from the server it protects and measure whether the restore finishes inside the required recovery window. PostgreSQL supports each major version for five years. Minor upgrades within a major do not require dump and restore, although release notes can still require extra steps. Major upgrades normally use `pg_upgrade` or dump and restore because data-directory compatibility can change. Test extensions and query plans before the production cutover. ## Common Pitfalls ### Using the Superuser for the Application Symptom: an application bug can alter schemas or bypass ordinary permission boundaries. Cause: the web process connects as a superuser or object owner. Fix: create a restricted runtime role, grant only required schema and table access, and run migrations through a separate identity. ### Trusting the Search Path Symptom: a migration or job reads the wrong object after a schema is added or reordered. Cause: unqualified names depend on a session's `search_path`. Fix: use schema-qualified names in migrations and background jobs, and set a deliberate path for application sessions. ### Hiding Core Fields in JSONB Symptom: joins, reports, uniqueness checks, and permissions repeatedly extract the same JSON keys. Cause: stable relational facts were stored inside one flexible payload. Fix: move identifiers, money, states, and common filters into typed columns with constraints. ### Adding Indexes Without a Query Symptom: writes slow down while the intended read still uses a scan. Cause: an index was chosen by column name rather than a real predicate and sort order. Fix: run `EXPLAIN` on the target query and add the smallest index that matches its access pattern. ## Frequently Asked Questions ### Is PostgreSQL 19 ready for production? PostgreSQL 19 was not ready for production on July 29, 2026. Beta 2 was a development release, while PostgreSQL 18.4 was the current stable documentation release. Use a supported stable major version, keep it on the current minor release, and test application plus extension compatibility before upgrading. ### What is a PostgreSQL schema? A schema is a namespace inside a database. It can contain tables, views, functions, types, and other objects. Schemas help separate ownership and names, but permissions and a controlled search_path still matter. Schema-qualified names make migrations and background jobs easier to review. ### When should an application use JSONB? Use JSONB for variable payloads whose fields differ by event or integration type. Keep identifiers, relationships, money, permissions, common filters, and reporting fields in typed columns with constraints. JSONB can be indexed, but indexing does not restore a relational model hidden inside one document. ### Does a PostgreSQL major upgrade require planning? A PostgreSQL major upgrade requires deliberate planning. Major versions can change the data-directory format, so an upgrade normally uses pg_upgrade or dump and restore. Verify extensions, read release notes, test migrations and query plans on production-shaped data, preserve a rollback path, and rehearse the complete cutover before production. ## What to Read Next Continue with [SQL schema design basics](/programming/sql/schema-design/) for keys and constraints, then use [SQL transactions and ACID](/programming/sql/transactions/acid/) to define multi-statement writes. Compare [MySQL vs PostgreSQL vs SQLite](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/) when the deployment choice remains open. Return to the [SQL databases hub](/programming/sql/databases/) for the other database guides. --- ## SQLite: CLI Inspection, WAL Mode, Backup, and Migration URL: /programming/sql/databases/sqlite/ Cluster: Programming > Sql SQLite is an embedded relational database engine. The application reads and writes a database file directly, without connecting to a separate database server. Choose SQLite when the data belongs to one application or device and writes can take turns. A single file still needs backups, migrations, and a restore plan. ## How SQLite Began D. Richard Hipp originally developed and released SQLite in 2000. The official release chronology starts with SQLite 1.0 on August 17, 2000. SQLite 3.0 arrived in 2004 with the modern file format and support for binary data plus Unicode. The project publishes its source and documentation in the public domain. Its self-contained library and stable file format helped it spread through phones, desktop software, browsers, devices, command-line tools, and test environments. That history explains why SQLite appears inside applications instead of beside them as another service. It does not make every workload local-file shaped. Concurrency, backup, permissions, and deployment topology still decide whether the fit is sound. ## Where SQLite Fits SQLite fits data that belongs close to one application or device. It can replace a custom JSON, XML, or binary project file while adding SQL queries, constraints, transactions, and indexes. SQLite is a strong fit for these uses: - Mobile and desktop application state. - Command-line history, caches, and searchable local data. - Test databases that need no server provisioning. - Embedded devices and application file formats. - Small websites on one host with short, modest writes. A client/server database fits better when data lives across a network, several hosts need to write concurrently, or database roles and central operations are part of the requirement. Compare [PostgreSQL](/programming/sql/databases/postgresql/) and [MySQL](/programming/sql/databases/mysql/) before adding workarounds for a deployment that has outgrown one file. ## How to Open and Inspect a Database The examples below target SQLite 3.53.4, released on July 24, 2026 and current on July 29, 2026. Check the installed library because operating systems and language runtimes can bundle a different SQLite version. ```bash sqlite3 --version sqlite3 app.db ``` Inside the shell, inspect the file before changing it: ```text .databases .tables .schema .headers on .mode column SELECT sqlite_version(); .quit ``` The dot-prefixed lines are commands understood by the `sqlite3` shell, while `SELECT sqlite_version();` is the SQL statement sent to the database. Create a small notes table with required fields and a stable sort key: ```sql CREATE TABLE notes ( id INTEGER PRIMARY KEY, title TEXT NOT NULL, body TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX notes_created_idx ON notes (created_at DESC, id DESC); INSERT INTO notes (title, body) VALUES ('Backup check', 'Restore the notebook before trusting the copy.'); SELECT id, title, created_at FROM notes ORDER BY created_at DESC, id DESC; ``` `INTEGER PRIMARY KEY` identifies the row using SQLite's integer row identifier. The composite index matches the sort order and uses `id` to break timestamp ties. The same CLI can inspect a backup or reproduce a migration without first writing application code. ## How WAL Mode Changes Concurrency SQLite uses a rollback journal by default. Write-ahead logging, or WAL, appends changes to a separate log before checkpointing them into the main database file. ```sql PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 5000; PRAGMA journal_mode; PRAGMA wal_checkpoint; ``` WAL lets readers continue while a writer is active in common cases, which can improve a read-heavy local application. It does not permit several simultaneous writers. SQLite still serializes writes, so long transactions make other writers wait. The main database file and the WAL state belong together until a checkpoint integrates the changes. All processes using a WAL database must run on the same host because the wal-index relies on shared memory. Do not place the database on a network filesystem to let several application servers write it. Automatic checkpoints handle many workloads, but a production review should still observe WAL growth, busy errors, checkpoint timing, shutdown behavior, and backup consistency. ## How to Back Up and Migrate SQLite Use SQLite-aware backup methods while the database is live. The command-line shell's `.backup` command uses the backup API rather than treating the active database as an ordinary file. ```bash sqlite3 app.db ".backup 'app-backup.db'" sqlite3 app-backup.db "PRAGMA integrity_check;" ``` An integrity check is useful, but a restore test must also open the application schema, run representative queries, and confirm that the backup contains the expected rows. Keep a protected copy away from the device or host it protects. Track schema changes in migrations and apply them inside transactions where SQLite supports the complete operation: ```sql BEGIN IMMEDIATE; ALTER TABLE notes ADD COLUMN archived_at TEXT; CREATE TABLE schema_migrations ( version TEXT PRIMARY KEY, applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP ); INSERT INTO schema_migrations (version) VALUES ('2026-07-29-add-archived-at'); COMMIT; ``` `BEGIN IMMEDIATE` acquires the write transaction before migration work begins, so a lock conflict appears before partial work. Rehearse table-rebuild migrations on a realistic backup because some schema changes require a new table, copied rows, and a controlled rename. ## Common Pitfalls ### Sharing the File over a Network Filesystem Symptom: WAL mode fails or database access becomes unreliable across hosts. Cause: several machines are treating one file as a network database. Fix: keep SQLite and every accessing process on one host, or move shared network data to a client/server database. ### Holding Write Transactions Open Symptom: other writes return busy errors or wait for noticeable periods. Cause: application code performs slow work while a write transaction remains open. Fix: prepare external work first, keep database writes short, commit promptly, and set a measured busy timeout. ### Copying Only the Main File During a Write Symptom: a copied database is missing recent changes or fails verification. Cause: a file copy ignored active journal or WAL state. Fix: use the online backup API, the shell's `.backup` command, or another documented SQLite-aware method, then test the copy. ### Treating One File as No Operations Symptom: an application update loses data or cannot restore an older schema. Cause: the single-file model was mistaken for a system without backups or migrations. Fix: document the file location, migration ledger, backup schedule, restore command, retention, and recovery owner. ## Frequently Asked Questions ### Is SQLite a relational database? SQLite is a relational database with transactional SQL support. It implements tables, indexes, triggers, views, constraints, and joins. The engine is embedded in the application process instead of running as a separate server. ### Can SQLite run a website? SQLite can run a website when one application host, modest write concurrency, and a local database file match the workload. A website that needs several writing hosts, database-level user administration, replicas, or heavy concurrent writes usually fits a client/server database better. ### Does WAL mode allow multiple writers? WAL mode still serializes every database write. It lets readers continue while a writer appends changes. Keep write transactions short, set a suitable busy timeout, and measure lock waits. WAL also requires all processes using the database to remain on the same host. ### When should an application move from SQLite? Move when the deployment no longer fits one local database file: several application servers need writes, lock waits affect users, centralized roles or replicas are required, or managed recovery becomes necessary. The trigger is a changed workload or operating model, not SQLite's age. ## What to Read Next Use [SQL schema design basics](/programming/sql/schema-design/) to strengthen keys and constraints, then compare [MySQL vs PostgreSQL vs SQLite](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/) before choosing a server database. Return to the [SQL databases hub](/programming/sql/databases/) for all four database guides. --- ## Import CSV into SQL: Load, Validate, and Transaction Paths URL: /programming/sql/imports/csv-to-sql/ Cluster: Programming > Sql Treat the file as an import pipeline rather than a quick string conversion. Parse the CSV with a real parser, load it into a staging table, validate it before touching the real tables, and use prepared statements or a [database](/programming/sql/)-native bulk loader depending on where the file comes from and how much review it needs. The problem is ordinary and it does not go away. Someone has a spreadsheet export and needs it inside a database, and the next person inherits product data, contact lists, or supplier exports from another system. The import that looks like a one-off script is usually the first version of a data pipeline. ## Start with a Staging Table Do not import a stranger's spreadsheet straight into the table your application depends on. The safer first step is a holding area: ```sql CREATE TABLE imported_contacts_stage ( batch_id VARCHAR(36) NOT NULL DEFAULT 'manual', line_number INTEGER, email VARCHAR(255), full_name VARCHAR(255), raw_status VARCHAR(100), imported_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); ``` That table has a batch, a source line number, and columns close to the CSV file. It remains a staging model rather than the final data model. It is a place to inspect, reject, deduplicate, and transform rows before they get near `contacts`, `users`, `orders`, or whatever else your application trusts. The defaults keep the table friendly to database-native bulk loaders. In a real application, add the key or unique constraint that matches your database and import process. This is the same discipline as [schema design basics](/programming/sql/schema-design/), only applied to incoming data. A CSV file often has no real types, no foreign keys, no useful constraints, and no record of why row 418 looks different from row 417. A staging table gives you somewhere to find out. ## Parse the File Correctly The first mistake is still the oldest one: ```php setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY); $file->setCsvControl(",", "\"", ""); foreach ($file as $row) { if ($row === [null] || $row === false) { continue; } // Validate and import the parsed row here. } ``` The empty escape string is there on purpose. PHP 8.4 deprecated relying on the default `escape` value for CSV parsing, and the PHP manual recommends setting it explicitly when round-tripping RFC 4180-style files matters. If the upload can be large, keep this streaming shape. Do not read the whole file into an array just because the first test file had twelve rows. ## Import with PHP and PDO Use a PHP import loop when the file is uploaded by a user, needs row-by-row validation, or must attach application metadata such as the current account. The database still receives values through a prepared statement: ```php setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY); $file->setCsvControl(",", "\"", ""); $insert = $pdo->prepare( "INSERT INTO imported_contacts_stage (batch_id, line_number, email, full_name, raw_status) VALUES (:batch_id, :line_number, :email, :full_name, :raw_status)" ); $imported = 0; $lineNumber = 0; $pdo->beginTransaction(); try { foreach ($file as $row) { $lineNumber++; if ($row === [null] || $row === false) { continue; } if ($lineNumber === 1 && strtolower((string) $row[0]) === "email") { continue; } [$email, $name, $status] = array_pad($row, 3, ""); $email = trim((string) $email); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { continue; } $insert->execute([ "batch_id" => $batchId, "line_number" => $lineNumber, "email" => strtolower($email), "full_name" => trim((string) $name), "raw_status" => trim((string) $status), ]); $imported++; } $pdo->commit(); } catch (Throwable $error) { $pdo->rollBack(); throw $error; } return $imported; } ``` This is not the fastest possible loader. It is the clearer loader when each row needs application judgment. Prepared statements keep CSV values out of raw SQL strings, and the transaction keeps a failed import from leaving half a batch in the staging table. For a very large upload, commit in batches and record enough progress to resume or retry safely. ## Upload Handling Keep upload handling and database inserts separate. First accept the upload, give it a server-owned name, and store it outside the public document root: ```php Sql Add an index only for a query you can point to, and read the plan before and after. An index helps the database find rows without scanning the whole table, and every one you add costs writes. PostgreSQL puts the tradeoff plainly: indexes can retrieve specific rows much faster, but they add overhead to the database and should be used sensibly. SQLite says the same thing from the planner side: [SQL](/programming/sql/) is declarative, so the database decides how to compute the result, and programmers give it useful indexes to choose from. ## A Simple Index ```sql CREATE INDEX users_email_idx ON users (email); ``` This helps a query that searches by the indexed email column: ```sql SELECT id, email FROM users WHERE email = 'mark@example.com'; ``` Without an index, the database may need to scan every row. With the index, it can look up the matching email more directly. The mental model is an old one: an index is the book index at the back, not the chapter itself. The index tells you where to look, but the table still owns the row. An index is another data structure the database has to maintain. When the indexed value changes, the index changes too. When a row is inserted or deleted, the index has to stay in sync with it. ## Index Types For most application work, a B-tree index is the default starting point. PostgreSQL's index documentation describes B-tree as the fit for equality and range comparisons, which is why it works for ordinary predicates like `=`, `<`, `>`, `BETWEEN`, and ordered scans. That maps cleanly to normal web queries: ```sql SELECT id, email, created_at FROM users WHERE created_at >= DATE '2026-01-01' ORDER BY created_at DESC; ``` A B-tree index on `created_at` can help because the query asks for an ordered range. Hash indexes are built for a narrower job. They are built for equality lookups, not range scans or ordering. If all you need is "find the row where this value equals that value," a hash index may be relevant in databases that support it well. In most PHP/MySQL/PostgreSQL application work, I still start with the database's ordinary B-tree index unless a measured plan says otherwise. Specialized indexes belong to more specialized jobs. PostgreSQL has GIN, GiST, BRIN, and other options for full-text search, arrays, ranges, geospatial work, and very large naturally ordered tables. Those are powerful tools, but they belong after the basic query shape is understood. If the query is a normal lookup, join, or ordered list, solve that normal problem first. ## Choosing Indexes Do not add indexes because a column "seems important." Add indexes because a query needs them. Good candidates usually come from queries you can point to: - Columns used in `WHERE` filters. - Foreign keys used in joins. - Columns used for common `ORDER BY` patterns. - Composite keys that match real multi-column lookups. Weak candidates usually come from anxiety rather than evidence: - Tiny tables. - Columns with only a few values, unless combined with another column. - Columns that are written constantly but rarely searched. - Every column in the table, which is a maintenance bill disguised as preparation. The question I ask in review is blunt: show me the query. If the query is real and repeated, we can talk about an index. If the answer is "we might search by that someday," the index can wait until someday has a workload attached to it. ## Composite Indexes Column order matters because composite indexes are read from the left side outward. ```sql CREATE INDEX orders_user_created_idx ON orders (user_id, created_at); ``` This index fits a query that filters by user and sorts by creation time: ```sql SELECT id, total_cents, created_at FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20; ``` The same index may not help much if the query only filters by `created_at`. Think from the left side of the index outward. That left-side rule is where many well-meant indexes go wrong, and the next example shows why the first column matters: ```sql CREATE INDEX orders_status_created_idx ON orders (status, created_at); ``` fits a query that starts with status: ```sql SELECT id, total_cents FROM orders WHERE status = 'paid' ORDER BY created_at DESC LIMIT 50; ``` It is a weaker fit for a query that ignores status and only asks for the latest orders: ```sql SELECT id, total_cents FROM orders ORDER BY created_at DESC LIMIT 50; ``` The database may still use the index in some cases, but the index was not shaped for that query. If both queries matter, you may need a different index, a partial index, or a product decision about which path deserves the write cost. ## Covering Indexes A covering index contains enough columns for the database to answer the query from the index without visiting the table row, or with fewer table visits depending on the database and visibility rules: ```sql CREATE INDEX orders_user_created_cover_idx ON orders (user_id, created_at) INCLUDE (total_cents, status); ``` That shape can support a narrow list query: ```sql SELECT created_at, total_cents, status FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20; ``` The indexed key columns are still the lookup and ordering path. The included columns are there so the database has the values the page needs. That is useful for high-traffic read paths, but it is not free. Included columns make the index larger, and larger indexes cost memory, cache, disk, backup time, and write maintenance. If the page needs twenty columns, a covering index is probably the wrong answer. Fetch the row from the table and cover the small hot path, not the whole object. ## Partial and Expression Indexes Some useful indexes only cover part of a table. A partial index can target the rows a query actually uses: ```sql CREATE INDEX orders_unshipped_created_idx ON orders (created_at) WHERE shipped_at IS NULL; ``` That can help an admin screen that repeatedly asks for unshipped orders: ```sql SELECT id, created_at, total_cents FROM orders WHERE shipped_at IS NULL ORDER BY created_at ASC LIMIT 100; ``` The tradeoff is that the query predicate has to match the index predicate closely enough for the planner to use it. Partial indexes are excellent when a small active subset of the table gets most of the reads, such as open tickets, unpaid invoices, active sessions, pending jobs, or unshipped orders. Expression indexes are useful when the query searches on a computed value: ```sql CREATE INDEX users_lower_email_idx ON users (lower(email)); ``` That supports the matching expression in the query: ```sql SELECT id, email FROM users WHERE lower(email) = lower('Mark@example.com'); ``` Use expression indexes deliberately, because they often describe a real business rule. If the business rule is "emails are unique case-insensitively," the index may need to be unique, and the application should normalize input consistently. The database can help enforce the rule, but the rule still needs to be named. ## Read the Plan Use `EXPLAIN` to see the plan the database chose. ```sql EXPLAIN SELECT id, total_cents FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20; ``` On shared hosting in the early 2000s, the raw output of a plain `EXPLAIN` was often all I had: no query analyzer, no dashboards, just reading the access type and deciding whether a sequential scan on a growing orders table was going to survive the week. Use `EXPLAIN ANALYZE` when you need actual run-time information, but remember that it runs the query. PostgreSQL's documentation warns that side effects still happen for data-changing statements, so wrap dangerous tests in a transaction and roll them back. ```sql BEGIN; EXPLAIN ANALYZE UPDATE orders SET reviewed = true WHERE created_at < now() - interval '1 year'; ROLLBACK; ``` That extra transaction is the difference between observing a plan and changing production data because you were curious. ## Reading EXPLAIN Output Plan output can look intimidating because it exposes the database's internal choices. Start with a few questions instead of trying to memorize every node type. First, did the database scan the whole table or use an index? ```text Seq Scan on orders Index Scan using orders_user_created_idx on orders ``` A sequential scan is not automatically bad. If the table is tiny, scanning it can be cheaper than using an index. If the query needs a large percentage of the table, a scan may be rational. The problem is a sequential scan on a large table when the page only needs a small slice. Second, compare estimated rows with actual rows when using `EXPLAIN ANALYZE`. A plan that expected 10 rows and got 100,000 is operating with a bad picture of the data. That can mean stale statistics, skewed data, a predicate the planner cannot reason about well, or an index that does not match the query shape. Third, look for sort work in the plan. A query can filter quickly and still slow down because `ORDER BY` forces a large sort after the filter. The right composite index can often handle both the filter and the order. Finally, check how the plan orders its joins. If the plan multiplies rows early and filters late, the index may not be the first problem. Read [SQL joins explained](/programming/sql/joins/) before tuning a join-heavy query in isolation. ## Look for the Mismatch When a query is slow, compare what you intended with what the planner did: - Did it scan the whole table? - Did it use the index you expected? - Did it estimate 10 rows and read 100,000? - Did a join multiply rows before filtering? - Did `ORDER BY` force a sort after the filter? The planner is usually rational with the statistics it has. If the stats are stale, the plan can look strange. If the query shape fights the indexes, the plan can be expensive while still being correct. ## Statistics and Data Shape The planner chooses a plan based on what it believes about the table. If the statistics are wrong, the plan can be wrong in a way that still looks logical. This matters most when data is uneven. A `status` column with values like `draft`, `paid`, `cancelled`, and `refunded` may look low-cardinality, but the distribution can still matter. If 98 percent of orders are `paid`, an index on `status` may not help a `paid` query much. It may help a `refunded` query if that value is rare. This is why real plans beat theory. Check the query that hurts, against data that resembles production, after the database has current statistics. Otherwise you are tuning a story about the data rather than the data itself. ## Indexes Cost Writes Every insert, update, and delete has to maintain the relevant indexes. On read-heavy tables, that cost is often worth it. On write-heavy tables, too many indexes can make the system slower in the name of speed. This is the practical rule. Add the index that supports a known query, check the plan, and stop before the schema turns into an index museum. Indexes also cost operational simplicity over time. They take disk space, enlarge backups, slow some migrations, make bulk imports slower, and can hide a table-design problem long enough for that problem to become expensive. There is a difference between "this table has many indexes because the product has many real read paths" and "this table has many indexes because nobody wanted to decide which query matters." The first is a cost of doing business. The second is technical debt with SQL syntax. ## Tuning Workflow When a page or report is slow, work in this order: 1. Identify the exact SQL and the exact user path. 2. Run the query against data that resembles production. 3. Read the plan with `EXPLAIN` or `EXPLAIN ANALYZE`. 4. Check whether the table shape and joins make sense. 5. Add or adjust one index that matches the measured query. 6. Re-run the plan and compare the result. 7. Remove redundant indexes once the better one is in place. That last step is easy to skip. If a new composite index fully covers the old single-column index's job, the old one may be dead weight. Do not delete indexes casually, but do audit them. A database that only accumulates indexes eventually becomes slower to write, harder to migrate, and more expensive to operate. ## PHP Symptoms In PHP apps, missing indexes usually show up as slow admin pages, slow search filters, or reports that worked fine with 500 rows and fall over at 500,000. The [PHP and MySQL with PDO](/programming/php/mysql/pdo/) layer did not suddenly become worse. The table got large enough for the database plan to matter. When that happens, keep the stack in order: 1. Check the SQL. 2. Check the plan. 3. Add or adjust the index. 4. Only then start blaming the framework. If the query joins multiple tables, read [SQL joins explained](/programming/sql/joins/) before tuning the index in isolation. If every query needs a heroic index to survive, step back to [schema design basics](/programming/sql/schema-design/) and check whether the table shape is doing the wrong job. The same warning applies to AI-generated SQL. The generated query may be syntactically correct and still ask the database to do work in the most expensive possible shape. Review the table relationships, the filters, the order, and the plan before accepting the patch. When one request writes related rows, pair indexing work with [transactions and ACID](/programming/sql/transactions/acid/). Fast reads do not compensate for half-written state. ## Frequently Asked Questions ### Does a primary key create an index automatically? Yes. Declaring a primary key creates a unique index on those columns in every mainstream database, so adding a second index on the same columns in the same order is redundant. Unique constraints also create an index that queries can use. ### What is the difference between a clustered and a non-clustered index? A clustered index determines the physical order of the rows, so a table has at most one. InnoDB clusters on the primary key. A non-clustered index is a separate structure holding the indexed columns and a pointer back to the row. ### Should you index a column with only a few distinct values? Usually not on its own. When a column such as a status flag matches a large share of the table, the planner often decides a scan is cheaper than the index lookups plus row fetches. Such columns are more useful as part of a composite index. ### Do indexes help with ORDER BY and GROUP BY? They can. An index already holds its columns in order, so the database may read them in sequence and skip a sort entirely. This only works when the ordering matches the index, including the direction and the leading columns. ### Should you index foreign key columns? Usually yes. Joins and lookups filter on those columns constantly, and some engines require an index to check the constraint efficiently. InnoDB creates one automatically for foreign keys; other databases may leave it to you. --- ## Common SQL Interview Questions URL: /programming/sql/interview-questions/ Cluster: Programming > Sql Going into a [SQL interview](/programming/sql/), you will be asked a small set of questions that barely changes between companies. Interviewers want to know whether you can be trusted with the data behind a product, so they ask about tables, joins, nulls, indexes, and transactions. That predictability is good news for anyone preparing, because it means the basics are worth knowing properly rather than approximately. An interviewer usually knows within two answers whether a candidate has read about SQL or written it. Knowing what a foreign key does is one thing. Saying what happens to the child rows when the parent is deleted is the thing that gets you the offer. The questions below are grouped by level. Start at the beginner section for a first developer or analyst role, and read the later sections for roles where you own schema, performance, or data integrity. Examples use PostgreSQL syntax; the concepts are portable. ## How to Answer a SQL Interview Question Start with the shape of the data before naming syntax. Define the result grain, the relationships, the constraints, and the database engine in play. A practical answer usually has four parts: 1. State the concept in plain language. 2. Show a small query or table definition. 3. Explain the failure mode. 4. Name how you would verify the answer. An index answer should mention the query pattern and `EXPLAIN`. A transaction answer should name the rollback boundary. A join answer should predict row multiplication. Those details expose the reasoning plan instead of presenting a memorized result. ## Beginner Questions These are the questions asked first in almost every screen. They look simple, and that is the trap. The interviewer is listening for whether the definition comes with a consequence attached. ### 1. What is SQL, and what is it used for? SQL is the language for working with relational facts: rows, tables, relationships, constraints, and sets. It is not only a way to fetch records for an application. A strong answer covers SQL's role in reading, changing, relating, and protecting stored facts. Tables keep different facts separate, while queries and constraints connect them without forcing everything into one record. ### 2. What is the difference between a table, a row, and a column? A table describes one kind of fact. A row is one instance of that fact. A column is one attribute of that fact. ```sql CREATE TABLE users ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, email TEXT NOT NULL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); ``` Here `users` is the table, each user is a row, and `email` is a column. The important interview detail is table meaning. If a table tries to describe two different facts at once, queries become harder and constraints become weaker. ### 3. What is the difference between SQL and NoSQL? A relational SQL database stores data in tables with defined columns. Primary keys identify rows, foreign keys connect tables, constraints reject invalid values, and transactions protect related changes. NoSQL is an umbrella label rather than one product. It covers document databases, key-value stores, graph databases, wide-column stores, and search engines, each with a different API and different guarantees, so a claim about one family does not automatically apply to another. The answer an interviewer wants is a workload answer, not a winner. Relational SQL suits stable entities whose relationships must stay valid, such as customers and orders. A specific NoSQL model suits a clear access pattern, such as reading one nested document or fetching a value by key. The [SQL vs NoSQL comparison](/programming/sql/comparisons/sql-vs-nosql/) works through the criteria in full. ### 4. What is the difference between MySQL, MariaDB, PostgreSQL, and SQLite? All four are relational databases and all four run SQL, so the differences that matter in an interview are the operational ones. - [MySQL](/programming/sql/databases/mysql/) is a client/server database, the long-time default for web applications, and widely hosted and documented. - [MariaDB](/programming/sql/databases/mariadb/) is an open-source database in the MySQL family. It speaks the MySQL client protocol, while keeping its own releases, features, storage engines, and compatibility rules, so compatibility is something you test rather than assume. - [PostgreSQL](/programming/sql/databases/postgresql/) is an object-relational database that combines SQL with a broad set of types, constraints, indexes, and extensions. - [SQLite](/programming/sql/databases/sqlite/) is an embedded engine. The application reads and writes a database file directly, without a separate database server. Name the shape first, server or file, then say which engine the role actually uses and where its syntax differs from the others. The [database engines guide](/programming/sql/databases/) covers each one on its own page. ### 5. What is a primary key? A primary key identifies a row and should be stable, unique, and present. In most application tables, a generated integer or UUID primary key is a practical choice. Business identifiers can still be unique without becoming the relationship key: ```sql CREATE TABLE products ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, sku TEXT NOT NULL UNIQUE, name TEXT NOT NULL ); ``` The `id` is the stable relationship key, while the unique `sku` remains a business identifier that can follow its own change rules. ### 6. What is a foreign key? A foreign key enforces that values in one table match values in another table. ```sql CREATE TABLE orders ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users (id), total_cents INTEGER NOT NULL ); ``` Now an order cannot point at a missing user. That is the database doing useful work instead of leaving the rule as a comment in application code. A complete answer also names the delete rule: restrict the parent delete, cascade it to dependent children, or set an optional reference to null according to the relationship's meaning. ### 7. What does `NULL` mean in SQL? `NULL` means the value is unknown, missing, or not applicable. It does not mean an empty string, zero, or false. That distinction matters because comparisons with `NULL` use different syntax: ```sql SELECT id, email FROM users WHERE deleted_at IS NULL; ``` This is not the same as `deleted_at = NULL`. A strong answer also notes that nulls affect aggregates, left joins, and engine-specific uniqueness behavior. ### 8. What is the difference between an inner join and a left join? An inner join returns only rows where the relationship matches. A left join returns every row from the left table and fills unmatched right-side columns with `NULL`. ```sql SELECT users.email, orders.id FROM users JOIN orders ON orders.user_id = users.id; ``` That query excludes users with no orders because there is no matching order row. ```sql SELECT users.email, orders.id FROM users LEFT JOIN orders ON orders.user_id = users.id; ``` That query keeps users with no orders, and the order columns are `NULL` for those rows. Use an inner join when the match must exist and a left join when missing relationships belong in the result. The [SQL joins guide](/programming/sql/joins/) covers the reporting mistakes around that choice. ### 9. What is the difference between `WHERE` and `HAVING`? The `WHERE` clause filters rows before grouping, while `HAVING` filters grouped results after aggregation. ```sql SELECT user_id, COUNT(*) AS order_count FROM orders WHERE status = 'paid' GROUP BY user_id HAVING COUNT(*) >= 3; ``` The `WHERE` clause limits the input rows to paid orders. The `HAVING` clause then keeps only users with at least three paid orders. Use `WHERE` for an input-row condition and `HAVING` for a condition on the grouped result. ### 10. What is the difference between `DELETE`, `TRUNCATE`, and `DROP`? `DELETE` removes rows from a table, usually with a `WHERE` clause. `TRUNCATE` removes all rows quickly in engines that support it. A `DROP` statement removes the table object itself. Interview-relevant details are engine-dependent: `TRUNCATE` typically bypasses per-row `DELETE` triggers, and in most engines it resets auto-increment counters, although some engines require an option or behave differently. A strong answer includes caution. Check the target rows first (the interval literal below is PostgreSQL syntax; MySQL writes it as `INTERVAL 90 DAY`): ```sql SELECT id, email FROM users WHERE deleted_at < CURRENT_DATE - INTERVAL '90 days'; ``` Then apply the same condition to the destructive statement inside the appropriate operational safeguards. ## Intermediate Questions From here the interviewer stops asking what things are and starts asking what happens. The answers that land are the ones that predict a result before writing the query. ### 11. What is the difference between `COUNT(*)` and `COUNT(column)`? A `COUNT(*)` expression counts rows, while `COUNT(column)` counts non-null values in that column. ```sql SELECT COUNT(*) AS total_rows, COUNT(deleted_at) AS deleted_rows FROM users; ``` If `deleted_at` is null for active users, `COUNT(deleted_at)` counts only deleted rows. A strong answer states that null behavior. ### 12. Why did this count get inflated? Because a join multiplied rows before the aggregate ran. ```sql SELECT users.id, COUNT(*) AS row_count FROM users JOIN orders ON orders.user_id = users.id JOIN order_items ON order_items.order_id = orders.id GROUP BY users.id; ``` If one order has five items, the joined result has five rows for that order. Counting rows at that point counts items, not orders. A small row count makes that multiplication visible: | Stage | Rows | | Orders before join | 3 | | Rows after item join | 5 | Fix it by counting the thing you mean: ```sql SELECT users.id, COUNT(DISTINCT orders.id) AS order_count FROM users JOIN orders ON orders.user_id = users.id JOIN order_items ON order_items.order_id = orders.id GROUP BY users.id; ``` Name the result grain before choosing the aggregate: one row per user, order, or item. ### 13. What is normalization? Normalization is the discipline of separating facts so each table records one kind of thing, and repeated facts do not need to be updated in several places. A normalized order model separates users, orders, products, and order items, then connects them with keys. Denormalize deliberately for a measured report, cache, or historical snapshot with a defined refresh rule. The [schema design guide](/programming/sql/schema-design/) shows the full process. ### 14. What is a many-to-many relationship? A many-to-many relationship means each row on one side can relate to many rows on the other side, and the reverse is also true. The reliable implementation is a join table. ```sql CREATE TABLE post_tags ( post_id BIGINT NOT NULL REFERENCES posts (id), tag_id BIGINT NOT NULL REFERENCES tags (id), PRIMARY KEY (post_id, tag_id) ); ``` A comma-separated list cannot receive ordinary foreign keys or support clean joins and indexes. The join table makes each relationship a row. ### 15. What does an index do? An index gives the database a faster path to matching rows for a query pattern. It can help filters, joins, ordering, uniqueness, and some covering reads. A strong answer covers both sides: an index can speed specific reads, but it consumes storage, adds write work, and should match a real query pattern. ### 16. How would you index this query? ```sql SELECT id, total_cents, created_at FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20; ``` A reasonable first index starts with the filter and then supports the sort: ```sql CREATE INDEX orders_user_created_idx ON orders (user_id, created_at); ``` The equality filter leads the index, followed by the ordering column. Verify the choice with `EXPLAIN` and representative data. The [indexes and query optimization guide](/programming/sql/indexes/query-optimization/) covers composite and covering indexes. ### 17. What is a transaction? A transaction groups database steps into one all-or-nothing operation. ```php prepare( "UPDATE accounts SET balance_cents = balance_cents - :debit_amount WHERE id = :account_id AND balance_cents >= :minimum_balance" ); $credit = $pdo->prepare( "UPDATE accounts SET balance_cents = balance_cents + :amount WHERE id = :account_id" ); $pdo->beginTransaction(); try { $debit->execute(["debit_amount" => 5000, "account_id" => 1, "minimum_balance" => 5000]); if ($debit->rowCount() !== 1) { throw new RuntimeException("Debit account was not updated."); } $credit->execute(["amount" => 5000, "account_id" => 2]); if ($credit->rowCount() !== 1) { throw new RuntimeException("Credit account was not updated."); } $pdo->commit(); } catch (Throwable $error) { if ($pdo->inTransaction()) { $pdo->rollBack(); } throw $error; } ``` Check both affected-row counts and roll back explicitly because InnoDB can leave earlier successful statements active after a later statement error, while a zero-row update is not an SQL error. ACID names atomicity, consistency, isolation, and durability. The [transactions and ACID guide](/programming/sql/transactions/acid/) covers isolation and deadlocks in detail. ### 18. What is SQL injection? SQL injection happens when untrusted input becomes part of SQL syntax instead of a value bound to a parameter. This is the unsafe pattern most PHP code reviews should reject: ```php $sql = "SELECT * FROM users WHERE email = '$email'"; ``` The safer version uses a prepared statement: ```php $stmt = $pdo->prepare('SELECT id, email FROM users WHERE email = ?'); $stmt->execute([$email]); ``` Prepared statements keep values out of SQL grammar. The [PHP PDO guide](/programming/php/mysql/pdo/) shows the complete application boundary. ### 19. What is the difference between `UNION` and `UNION ALL`? `UNION` combines compatible result sets and removes duplicates. `UNION ALL` combines compatible result sets and keeps duplicates. ```sql SELECT email FROM newsletter_signups UNION ALL SELECT email FROM account_users; ``` Use `UNION ALL` when duplicates are meaningful or impossible by design. Use `UNION` when duplicate removal is required. The [UNION guide](/programming/sql/queries/union/) shows the result sets and column-shape rules. ## Advanced Questions These appear when the role owns performance, concurrency, or the review of somebody else's SQL. The interviewer is no longer checking knowledge; they are checking judgment under a trade-off. ### 20. What is a covering index? A covering index is an index that contains everything the query needs, so the database can answer the query from the index without reading the table row in the ordinary way. ```sql SELECT user_id, created_at FROM orders WHERE user_id = 42 ORDER BY created_at DESC; ``` An index on `(user_id, created_at)` may cover that query. If the query also selects `total_cents`, the database may need either the table row or an index design that includes that value. Covering indexes can help read-heavy paths, but they make the index larger and writes more expensive. ### 21. What does `EXPLAIN` show? `EXPLAIN` shows the plan the database expects to use for a query. It helps you see scan types, join order, index usage, estimated rows, sorts, and other planning choices. Read the plan with these questions in mind: - Is the database reading far more rows than expected? - Is it using the index you thought would help? - Is a sort happening after the filter? - Did a join multiply the work? - Are row estimates wildly wrong? A strong answer names both `EXPLAIN` and the evidence to inspect after running it. ### 22. What is a deadlock? A deadlock happens when transactions wait on each other in a cycle, so none of them can proceed. The database breaks the cycle by aborting one transaction. Deadlocks can happen in a correct busy system. These habits keep them smaller and easier to retry: - Keep transactions short. - Touch rows in a consistent order. - Retry the failed transaction when the operation is safe to retry. - Avoid doing network calls or slow application work while a transaction is open. A strong answer treats the aborted transaction as an expected retry case when the operation is safe to repeat. ### 23. How would you find the second highest salary? Clarify whether the question asks for a distinct salary value or an employee row, and define how ties should behave. If the question asks for the second distinct salary: ```sql SELECT DISTINCT salary FROM employees ORDER BY salary DESC LIMIT 1 OFFSET 1; ``` If the question asks for the employee row with the second-ranked salary, a window function is usually clearer: ```sql SELECT id, name, salary FROM ( SELECT employees.*, DENSE_RANK() OVER (ORDER BY salary DESC) AS salary_rank FROM employees ) ranked WHERE salary_rank = 2; ``` `DENSE_RANK()` keeps tied salaries at the same rank. ### 24. What is a window function? A window function computes a value across related rows without collapsing them into one grouped row. ```sql SELECT user_id, created_at, total_cents, SUM(total_cents) OVER ( PARTITION BY user_id ORDER BY created_at ) AS running_total_cents FROM orders; ``` The query keeps one row per order and adds a running total per user. A normal `GROUP BY` would collapse those rows. [SQL Window Functions](/programming/sql/queries/window-functions/) works through ranking, running totals, `LAG`, and frames with runnable examples. ### 25. How do you review SQL generated by an ORM or AI assistant? Read the SQL, not only the application method name. The generated query is the contract the database has to execute. A practical review checks these points: - Does it use parameters instead of string concatenation? - Does the join match the actual relationship? - Does it accidentally select every column? - Does pagination happen in SQL rather than in memory? - Does the filter match an index? - Does it run one query or one query per row? - Does a write sequence need a transaction? The review should end with the same reasoning plan used throughout this page: expected row grain, constraints, access path, parameters, and transaction boundary. ## Mistakes Candidates Make Three habits cost more offers than missing knowledge does. Each one is a preparation problem rather than a SQL problem. ### Reciting Syntax Without Naming the Result Grain Symptom: a familiar query is offered immediately, but duplicate rows or ties change the answer. Cause: preparation focused on syntax instead of the required row shape. Fix: state whether the result is one row per user, order, value, or group before writing SQL. ### Treating One Engine's Behavior as Portable SQL Symptom: an answer relies on one engine's identifier quoting, date arithmetic, auto-increment syntax, or `LIMIT` behavior without naming it. Cause: vendor behavior was presented as universal SQL. Fix: name the engine, give the portable concept first, and identify the syntax that changes elsewhere. ### Claiming Performance Without Reading a Plan Symptom: an index or rewrite is declared faster without evidence. Cause: the answer stops at a plausible rule. Fix: describe the expected access path, run `EXPLAIN` on representative data, and compare rows read, estimates, sorts, and execution time where the engine supports it. ## What to Focus on the Night Before With one evening left, revise the handful of questions that turn up in almost every screen rather than the whole page: joins, `NULL` behavior, primary and foreign keys, what an index actually does, and the difference between `DELETE`, `TRUNCATE`, and `DROP`. For each one, practice the habit this whole page rests on. Say what happens as well as what the thing is. Name the row grain, the failure mode, and how you would check the result. Then pick three questions from above and answer them out loud tonight. ## Frequently Asked Questions ### Should an interview answer begin with SQL or plain language? Begin with one plain-language sentence that defines the concept or result. Add a small query when syntax makes the reasoning clearer, then explain the important row shape, failure mode, or verification step. This keeps the answer understandable before implementation details appear. ### How should you handle an ambiguous SQL question? State the ambiguity and ask which result the question requires. Clarify duplicate handling, null behavior, database engine, expected row grain, and tie rules before choosing syntax. If clarification is unavailable, name the assumption and show how another assumption would change the answer. ### Should you mention trade-offs in every answer? Mention a trade-off when the choice changes correctness, write cost, portability, concurrency, or maintainability. A basic definition does not need a forced downside. Indexes, denormalization, isolation levels, destructive operations, and generated SQL usually do need a clear cost or boundary. ### How can you practice without memorizing scripts? Create tiny tables, predict the rows each query will return, run the query, and explain any difference. Change one condition at a time. Practice naming result grain, null behavior, duplicate behavior, constraints, and the evidence from EXPLAIN instead of rehearsing one fixed sentence. ### Should you write SQL exactly as it would run in production during a whiteboard interview? Close is enough. The reasoning behind the query matters more than exact quoting, escaping, or dialect punctuation on a whiteboard. State any shortcut taken for the format, such as skipping parameter binding, so the interviewer knows it was deliberate. ### Is it acceptable to say you do not know the answer to a SQL interview question? Yes, when paired with a plan. Naming what to check, test, or look up to find the answer shows more capability than a confident guess. A wrong answer stated with certainty is worse than an honest gap paired with a clear next step. ### Do junior candidates need to know window functions and CTEs? Not usually. Interviewers generally expect solid joins, grouping, and basic performance reasoning at every level, while window functions, recursive queries, and advanced query patterns matter more as the role's scope and seniority grow. ## Self-Check Answer these out loud before reading the answers. Speaking the answer is closer to the interview than recognizing it on a page. Use this small grouped query for the first question: ```sql WITH employees (department_id) AS ( VALUES (10), (10), (20) ) SELECT department_id, COUNT(*) AS employee_count FROM employees GROUP BY department_id HAVING COUNT(*) >= 2; ``` 1. Predict the output: which department row and count does the query return? 2. Multiple choice: which clause filters groups after aggregation: `WHERE`, `HAVING`, or `ORDER BY`? 3. Multiple choice: which join keeps every row from the left table: inner join, left join, or cross join? 4. Multiple choice: which database runs without a separate server process: MySQL, PostgreSQL, or SQLite? 5. Predict the output: a table holds four users, and `deleted_at` is null for three of them. What do `COUNT(*)` and `COUNT(deleted_at)` return? 6. Multiple choice: which tool checks the planned access path: `EXPLAIN`, `COMMIT`, or `UNION`? 7. Short answer: a join between users, orders, and order items inflates an order count. What causes it, and what fixes it? 8. Short answer: an interviewer asks whether to use SQL or NoSQL for a new checkout system. What do you ask before answering? ### Answers 1. Department 10 with a count of 2. Department 20 has one row, so `HAVING` removes that group. 2. `HAVING`. It runs against grouped results after the aggregate is calculated. 3. Left join. Unmatched right-side columns become `NULL`. 4. SQLite. It is embedded, so the application reads and writes the database file directly. 5. 4 and 1. `COUNT(*)` counts rows, while `COUNT(deleted_at)` counts only the non-null values. 6. `EXPLAIN`. It reports the access plan selected by the database. 7. The item join multiplies rows before the aggregate runs. Count the thing you mean with `COUNT(DISTINCT orders.id)`, after naming the result grain. 8. Ask about the data and the access pattern. Checkout records have relationships and money movement, so the burden of proof sits with any non-relational alternative. ## Next Steps The fastest preparation is a small PHP and MySQL application, because a rough one exposes every topic on this page at once: - It pastes values into query strings. - It joins tables and accidentally duplicates rows. - It has no indexes on foreign keys. - It writes to several tables without a transaction. - It leaves referential rules in comments instead of constraints. Fix those five things and you will have answered most of the beginner and intermediate questions with your hands rather than your memory. Continue with [SQL joins](/programming/sql/joins/), [SQL schema design](/programming/sql/schema-design/), [SQL indexes and query optimization](/programming/sql/indexes/query-optimization/), and [SQL transactions and ACID](/programming/sql/transactions/acid/). The [PHP guide](/programming/php/) connects those database decisions to application code. --- ## SQL Joins: Inner, Left, Self Joins, and Explicit Syntax URL: /programming/sql/joins/ Cluster: Programming > Sql A join combines rows from more than one table. That is the plain version, and it is the only version worth starting with. [Relational data](/programming/sql/) usually lives in separate tables because the facts are separate: users, orders, line items, products, and categories each describe a different part of the system. Joins let you ask a question that crosses those boundaries without copying the same data into every table. ## The Basic Join ```sql SELECT orders.id, users.email, orders.total_cents FROM orders JOIN users ON users.id = orders.user_id; ``` The `ON` clause describes the relationship between the tables. In this case, every order points at the user who owns it. PostgreSQL's tutorial notes that older SQL can list tables in `FROM` and put the relationship in `WHERE`. The result can be the same, but explicit `JOIN ... ON` is easier to read because the relationship sits where the reader expects it. Use the explicit form unless you have a very specific reason not to. ## Inner Join `JOIN` by itself means inner join in ordinary usage. It returns rows where the join condition matches on both sides. ```sql SELECT users.email, orders.id AS order_id FROM users JOIN orders ON orders.user_id = users.id; ``` If a user has no orders, that user disappears from this result because the inner join only returns matched rows from both sides. ## Left Join Use a left join when you want every row from the left table, even if the right table has no match. ```sql SELECT users.email, orders.id AS order_id FROM users LEFT JOIN orders ON orders.user_id = users.id; ``` Now a user with no orders still appears, with `NULL` for the order columns. That distinction matters in reporting because "no matching row" and "matching row with a zero value" are not the same fact. ## Left-join Filters The fastest way to break a left join is to add a `WHERE` condition on the right-hand table without thinking through the `NULL` case. The query still runs, and that is part of the danger. This query looks like it should list every user and show paid orders when they exist: ```sql SELECT users.email, orders.id AS order_id FROM users LEFT JOIN orders ON orders.user_id = users.id WHERE orders.status = 'paid'; ``` That query does not keep every user. The `WHERE` clause runs after the join result has been built, so users without matching orders have `NULL` in `orders.status`. The condition removes them, and the query behaves like an inner join for this report. If the status filter belongs to the relationship, keep it in the `ON` clause: ```sql SELECT users.email, orders.id AS paid_order_id FROM users LEFT JOIN orders ON orders.user_id = users.id AND orders.status = 'paid'; ``` Now every user remains in the result. Users without a paid order get `NULL` for the order columns, which is the fact the report needs to preserve. There are times when the `WHERE` version is correct. If the business question is "which users have a paid order," an inner join or a filtered left join can both make sense. If the question is "show me all users and whether they have a paid order," the filter belongs inside the join condition. ## The Duplicate-row Trap A join returns matched pairs, so it can multiply rows when one side has many matches. If one user has five orders, this query returns five rows for that user: ```sql SELECT users.id, users.email, orders.id AS order_id FROM users JOIN orders ON orders.user_id = users.id; ``` That result is correct because the query asked for one row per matched pair. The trouble starts when a report expects one row per user but joins into a table with many related rows. Counts inflate, totals duplicate, and someone spends an afternoon blaming the charting library. Use aggregation when you want one row per parent: ```sql SELECT users.id, users.email, COUNT(orders.id) AS order_count FROM users LEFT JOIN orders ON orders.user_id = users.id GROUP BY users.id, users.email; ``` That query still has one important choice hidden inside it: `COUNT(orders.id)` counts only matching order rows, while `COUNT(*)` would count the left-side user row even when no order exists. For a left-joined count, count a non-null column from the right table unless you deliberately want to count parent rows. The same problem shows up with sums. If you join `orders` to `order_items` and then sum an order-level total, the order total appears once per line item. Sum the line-item values, or aggregate the child table first and join the smaller result back to the parent: ```sql SELECT users.id, users.email, COALESCE(order_totals.total_cents, 0) AS total_cents FROM users LEFT JOIN ( SELECT orders.user_id, SUM(orders.total_cents) AS total_cents FROM orders GROUP BY orders.user_id ) AS order_totals ON order_totals.user_id = users.id; ``` I like this pattern because it makes the grain of the result visible. The subquery has one row per user. The outer query also has one row per user. When those two statements disagree, the query is probably hiding a duplicate-row bug. ## Qualify Columns Once a query has more than one table, qualify the columns: ```sql SELECT users.email, orders.created_at FROM users JOIN orders ON orders.user_id = users.id; ``` The version below asks the reader to infer too much: ```sql SELECT email, created_at FROM users JOIN orders ON user_id = id; ``` It can also break later if another table gets a column with the same name. Aliases are useful when the table names get longer, but keep them readable. `orders AS o` and `order_items AS oi` are fine in a short query. A six-table report full of `a`, `b`, `c`, and `d` makes review harder than it needs to be. ```sql SELECT u.email, o.created_at, oi.sku FROM users AS u JOIN orders AS o ON o.user_id = u.id JOIN order_items AS oi ON oi.order_id = o.id; ``` The alias should reduce noise without hiding the relationship. If you have to scroll back up to remember what `x1` means, the alias has stopped helping. ## The USING Clause SQL also supports `USING` when both joined tables have the same column name: ```sql SELECT orders.id, order_items.sku FROM orders JOIN order_items USING (order_id); ``` That can be tidy when the schema uses consistent foreign-key names. It is also easier to abuse than `ON`, because it hides both sides of the equality. I prefer `ON` in teaching examples and code reviews because it says exactly which table owns each column. Use `USING` when the relationship is obvious and the team already uses that convention. Use `ON` when the column names differ, when the query crosses several tables, or when a reviewer needs every relationship spelled out. ## Self Joins A self join joins a table to itself. The usual example is an employee table where each employee row can point at another employee as a manager: ```sql SELECT employee.name AS employee_name, manager.name AS manager_name FROM employees AS employee LEFT JOIN employees AS manager ON manager.id = employee.manager_id; ``` This is still an ordinary join with clearer labels. The aliases are what make it readable. One copy of the table is being used as the employee side, and the other copy is being used as the manager side. Self joins also show up in category trees, threaded comments, referrals, and replacement records. They are useful when a row relates to another row of the same kind. They are painful when the relationship is really a hierarchy with arbitrary depth, because one join only walks one step. Recursive queries are a better fit once you need all descendants rather than the immediate parent. ## Cross Joins A cross join returns every combination of rows from the left and right tables. If one table has three sizes and another has four colors, the result has twelve rows: ```sql SELECT sizes.name AS size_name, colors.name AS color_name FROM sizes CROSS JOIN colors; ``` Cross joins are legitimate for generating combinations, calendar grids, test data, or pricing matrices. They are also what you accidentally create when you forget a join condition. This old-style query has no relationship between the tables: ```sql SELECT users.email, orders.id FROM users, orders; ``` If there are 10,000 users and 80,000 orders, that result starts at 800 million paired rows before any later filter rescues it. That kind of mistake is why explicit `JOIN ... ON` is more than style. It keeps the relationship visible at the point where the row multiplication begins. ## Many-to-many Joins A many-to-many relationship needs a join table. Products can have many tags, and tags can belong to many products, so neither table should store a comma-separated list of the other. ```sql CREATE TABLE product_tags ( product_id bigint NOT NULL, tag_id bigint NOT NULL, PRIMARY KEY (product_id, tag_id) ); ``` The query joins through that relationship table: ```sql SELECT products.name, tags.name AS tag_name FROM products JOIN product_tags ON product_tags.product_id = products.id JOIN tags ON tags.id = product_tags.tag_id; ``` The join table is part of the model. It is the fact that "this product has this tag." Once you need metadata on the relationship, such as who added the tag or when it was approved, the join table has an obvious home for those columns too. This is where [schema design basics](/programming/sql/schema-design/) and joins meet. A clean many-to-many table makes the query boring, which is usually the best outcome a database can give you. ## Anti-joins and Missing Rows Sometimes the useful result is the missing relationship. A left join plus `IS NULL` finds rows with no match on the right: ```sql SELECT users.id, users.email FROM users LEFT JOIN orders ON orders.user_id = users.id WHERE orders.id IS NULL; ``` That reads as "users with no orders." It works because matched users have an order id, while unmatched users have `NULL` for the right-hand columns. The same idea can often be written with `NOT EXISTS`: ```sql SELECT users.id, users.email FROM users WHERE NOT EXISTS ( SELECT 1 FROM orders WHERE orders.user_id = users.id ); ``` I reach for `NOT EXISTS` when the intent is absence, because it says the quiet part directly. The planner may produce a similar strategy either way, but the next developer reading the query gets a cleaner signal. ## Join Order and Indexes The database does not always execute the join in the same mental order you wrote it. The planner chooses a strategy based on statistics, indexes, and estimated row counts, which is exactly what you want from a mature database. It also means you should index columns that are regularly used to connect tables. For this query, the planner has to find the user's orders and then load the matching line items: ```sql SELECT orders.id, order_items.sku FROM orders JOIN order_items ON order_items.order_id = orders.id WHERE orders.user_id = 42; ``` The likely supporting indexes would start with the relationship and filter columns: ```sql CREATE INDEX orders_user_id_idx ON orders (user_id); CREATE INDEX order_items_order_id_idx ON order_items (order_id); ``` Indexes are not free, so read [indexes and query optimization](/programming/sql/indexes/query-optimization/) before adding them everywhere. For join performance, start with the columns in `ON` and the columns in `WHERE`. A foreign key column that is searched constantly usually deserves an index. A column that is only written and rarely searched may not. Then look at the plan rather than guessing. PostgreSQL's `EXPLAIN` output will show whether the database scans a whole table, uses an index, builds a hash table, or loops through matches. Those words matter less than the shape: which table is being reduced first, how many rows are estimated, and whether the estimate is close to reality. ```sql EXPLAIN SELECT orders.id, order_items.sku FROM orders JOIN order_items ON order_items.order_id = orders.id WHERE orders.user_id = 42; ``` If the estimate is wildly wrong, the issue may be stale statistics, a missing index, or a predicate the planner cannot reason about well. If the estimate is right and the query is still slow, the table shape or reporting requirement may need work. An index is a tool, not a confession booth for a confused schema. ## Joins and Data Ownership Every join says something concrete about ownership. `orders.user_id` says an order belongs to a user. `order_items.order_id` says a line item belongs to an order. A nullable foreign key says the relationship may be absent, and that absence has to mean something. When a query feels awkward, check whether the schema is saying the truth plainly: - If a value repeats across rows as text, it may need its own table. - If a join requires matching on names, dates, or descriptions, the relationship probably lacks a real key. - If a report needs one row per customer but every query explodes into children, aggregate at the child boundary before joining back. - If PHP code keeps patching missing relationships after the query runs, the database may be missing a constraint. That last point is where old LAMP habits can still teach something useful. We used to write more of this by hand, which meant bad relationships became visible fast. Modern frameworks can hide the join behind a relationship method, but the database still has to do the same work. ## A Review Checklist for Joins When I review a join-heavy query, I usually walk it in this order: 1. Identify the grain of the result: one row per user, order, line item, tag, or report period. 2. Check each `ON` clause and name the relationship in plain English. 3. Look for filters on right-hand tables after a left join. 4. Check whether any join can multiply rows before an aggregate. 5. Qualify columns and keep aliases readable. 6. Confirm the join columns have sensible indexes for the workload. 7. Run `EXPLAIN` once the query matters to performance. That checklist catches most join bugs before they turn into application bugs. It also gives AI-generated SQL a fair review. The query may be syntactically valid and still join the wrong facts. ## Joins in PHP Apps In a PHP app, joins usually appear after the first version ships. The first screen lists notes, the next screen needs the note author's email, and then the admin table needs counts. Suddenly the application is relational whether the code wanted to admit it or not. I went through that progression long before ORMs existed to soften the landing, building queries directly in phpMyAdmin until I understood exactly which foreign key column connected the tables. There was no abstraction layer to guess on your behalf. That is why [PHP and MySQL with PDO](/programming/php/mysql/pdo/) should lead into joins quickly. Prepared statements protect the values entering SQL, while joins decide whether the data you read is actually the data you meant. If the query changes more than one table, read [transactions and ACID](/programming/sql/transactions/acid/) next. If every join feels like a workaround, go back to [schema design basics](/programming/sql/schema-design/). Joins are where the relational model stops being vocabulary and starts telling you whether the application has modeled the work honestly. ## Frequently Asked Questions ### What is the difference between a JOIN and a UNION? A join combines columns from different tables into wider rows, matching them on a condition. A union stacks the rows of two result sets that already have the same column count and compatible types. Use a join to add related fields, and a union to append more rows. ### Does MySQL support FULL OUTER JOIN? MySQL does not. The usual workaround is a LEFT JOIN combined with a RIGHT JOIN using UNION, which also removes the rows counted twice. PostgreSQL, SQL Server, Oracle, and SQLite from version 3.39 support FULL OUTER JOIN directly. ### Is a LEFT JOIN slower than an INNER JOIN? Not inherently. A left join can cost more because the database must keep unmatched left-hand rows, which sometimes narrows the join orders it can choose. On indexed join columns the difference is often small. Check EXPLAIN rather than assuming one is faster. ### Should you use a subquery or a join? Use a join when the result needs columns from both tables. Use a subquery when you only need to test membership or produce a single value. Optimizers frequently rewrite one form into the other, so choose the one that states the intent more clearly, then confirm with EXPLAIN. ### Why is RIGHT JOIN used so rarely? A right join keeps unmatched rows from the second table rather than the first. Every right join can be written as a left join by swapping the table order, and reading left to right matches how most queries are built, so teams standardize on LEFT JOIN. ### How many tables can you join in a single query? MySQL allows up to 61 tables in one join and other engines allow more, so the limit is rarely the problem. The practical constraint is the optimizer's ability to find a good plan. A query joining more than roughly eight tables usually deserves a second look. ### Do you need a foreign key to join two tables? No. A join matches values in the columns you name, and any comparable columns work. A foreign key constrains the data so those matches stay valid. Without one the join still runs, but nothing prevents orphaned rows from quietly changing what the query returns. ## Self-Check 1. Which join keeps rows from the left table that have no match on the right? 2. What does a `WHERE orders.status = 'paid'` condition do to the unmatched rows of a LEFT JOIN? 3. Why can a join return more rows than the table you started from? 4. What does `USING (customer_id)` require that `ON a.customer_id = b.customer_id` does not? 5. Which join finds customers with no orders, and what condition makes it work? 6. Why does qualifying every column matter once a query joins three tables? ### Answers 1. LEFT JOIN. Unmatched right-hand columns come back as `NULL`. 2. It removes them. A null-rejecting condition fails against the `NULL`s, so the query quietly behaves like an INNER JOIN. When the unmatched left rows must remain, put the filter in the `ON` clause. 3. One row can match many. A one-to-many relationship repeats the left-hand row once per match, which is the duplicate-row trap. 4. The same column name in both tables. `USING` also returns that column once rather than twice. 5. An anti-join. LEFT JOIN the orders table, then filter for `IS NULL` on a column that can never be null in a real match. 6. Ambiguity. Two tables can share a column name, and a reader cannot tell which table a bare name belongs to. --- ## SQL CTEs: WITH and WITH RECURSIVE URL: /programming/sql/queries/common-table-expressions/ Cluster: Programming > Sql A common table expression, or CTE, gives a query a name so the rest of a statement can refer to it. An accountant's worksheet works the same way: labeled working lines come first, and the final figure refers back to them. A recursive CTE builds each new line from the previous one. Use a CTE when a query has steps a reader must follow in order, or when it must walk a hierarchy of unknown depth. Whether it also makes the query faster is a separate question that only the execution plan answers. The examples below compare salaries, walk a management hierarchy and fill missing dates. ## Before You Start - Run the sample-table script below in a fresh scratch database first. - Know how [joins](/programming/sql/joins/) connect rows and how [UNION and UNION ALL](/programming/sql/queries/union/) combine results. - The syntax outline is not executable, and the labeled T-SQL example needs SQL Server. ## What Is a Common Table Expression? A common table expression is a named query that exists only for the statement it belongs to. It is defined in a `WITH` clause before the main query, and the main query refers to it by name in the same places it could name a table. Three terms come up throughout this guide: - CTE name: the label on the worksheet line, such as `dept_avg`. It is visible only inside the one statement. - Anchor member: in a recursive CTE, the query that produces the starting rows without referring to the CTE, like the first line you write by hand. - Recursive member: the query that refers to the CTE's own name and produces the next rows from the rows the previous pass produced. ### The Sample Tables Apart from one counting example that needs no table, every example uses the two small tables below. The `employees` table stores a management hierarchy through `manager_id`, with salaries in thousands. The `orders` table has deliberate gaps in its dates for the series section. ```sql CREATE TABLE employees ( id INTEGER PRIMARY KEY, name VARCHAR(20) NOT NULL, manager_id INTEGER, dept VARCHAR(10) NOT NULL, salary INTEGER NOT NULL ); INSERT INTO employees VALUES (1, 'Ava', NULL, 'Exec', 250), (2, 'Ben', 1, 'Eng', 180), (3, 'Chloe', 1, 'Sales', 170), (4, 'Dan', 2, 'Eng', 140), (5, 'Erin', 2, 'Eng', 120), (6, 'Finn', 4, 'Eng', 95), (7, 'Gia', 3, 'Sales', 110), (8, 'Hugo', 3, 'Sales', 90); CREATE TABLE orders ( id INTEGER PRIMARY KEY, order_day DATE NOT NULL, amount INTEGER NOT NULL ); INSERT INTO orders VALUES (1, '2026-03-01', 120), (2, '2026-03-02', 80), (3, '2026-03-02', 40), (4, '2026-03-04', 200), (5, '2026-03-06', 60); ``` The column types are plain enough for SQLite, PostgreSQL, MySQL, and SQL Server to accept the script unchanged. Ava has no manager, Ben and Chloe report to her, and Finn sits three levels down under Dan and Ben. The reporting lines form the following hierarchy: ```text Ava ├── Ben │ ├── Dan │ │ └── Finn │ └── Erin └── Chloe ├── Gia └── Hugo ``` ### A CTE Compared with a Subquery Suppose the question is "who earns more than their department's average?" The first answer most people write puts the averages in a subquery inside the `FROM` clause: ```sql SELECT e.name, e.dept, e.salary, ROUND(d.avg_sal, 1) AS avg_sal FROM employees e JOIN ( SELECT dept, AVG(salary * 1.0) AS avg_sal FROM employees GROUP BY dept ) d ON d.dept = e.dept WHERE e.salary > d.avg_sal ORDER BY e.id; ``` The same query written with a CTE moves the averages to the top and gives them a name: ```sql WITH dept_avg AS ( SELECT dept, AVG(salary * 1.0) AS avg_sal FROM employees GROUP BY dept ) SELECT e.name, e.dept, e.salary, ROUND(d.avg_sal, 1) AS avg_sal FROM employees e JOIN dept_avg d ON d.dept = e.dept WHERE e.salary > d.avg_sal ORDER BY e.id; ``` Both queries return the same three rows: | name | dept | salary | avg_sal | | Ben | Eng | 180 | 133.8 | | Chloe | Sales | 170 | 123.3 | | Dan | Eng | 140 | 133.8 | The execution plan still depends on your database engine and data, so equivalent output alone does not prove equivalent performance. What changes is the reading order. The CTE version reads top to bottom, with the working line first and the question second. The `salary * 1.0` is there for SQL Server, where `AVG` over an integer column returns an integer and would report 133 instead of 133.75. The other three engines return a decimal average without it. ## How Do You Write a WITH Clause? The general shape is the keyword `WITH`, one or more named queries separated by commas, and then a single main statement that uses them: ```sql WITH cte_name [(column, ...)] AS ( SELECT ... )[, another_cte AS ( SELECT ... )] SELECT ... FROM cte_name ...; ``` - The name follows the same rules as a table alias. It can even match a real table's name, in which case the CTE hides the table for that statement, which is legal but confusing. - The optional column list renames the columns, as in `counter(n)`. It is useful when the inner query produces an unnamed expression such as `SELECT 1`. - The main statement is usually a `SELECT`, although all four engines also accept a `WITH` clause in front of `UPDATE` and `DELETE`. A CTE lives for exactly one statement. Once the statement finishes, the name is gone, so the next query has to define it again or use a view or temporary table instead. ## Using Multiple CTEs in One Query Several CTEs go in one `WITH` clause, separated by commas, and each one can read from the CTEs defined before it. This is where the worksheet comparison earns its keep, because each working line can build on the lines above it. This query counts, for each department, how many people earn more than that department's average. The first CTE calculates the averages, and the second CTE uses them: ```sql WITH dept_stats AS ( SELECT dept, COUNT(*) AS headcount, AVG(salary * 1.0) AS avg_sal FROM employees GROUP BY dept ), above_avg AS ( SELECT e.name, e.dept FROM employees e JOIN dept_stats d ON d.dept = e.dept WHERE e.salary > d.avg_sal ) SELECT d.dept, d.headcount, COUNT(a.name) AS above_avg FROM dept_stats d LEFT JOIN above_avg a ON a.dept = d.dept GROUP BY d.dept, d.headcount ORDER BY d.dept; ``` | dept | headcount | above_avg | | Eng | 4 | 2 | | Exec | 1 | 0 | | Sales | 3 | 1 | Notice that `dept_stats` is used twice: once inside `above_avg` and once in the main query. A subquery in the `FROM` clause, which MySQL's manual calls a derived table, would have to be written out twice to do the same thing. The `LEFT JOIN` keeps Exec in the result with a count of 0. Ava's salary equals her one-person department's average, so she does not count as above it. Define each CTE before the ones that read it. MySQL and SQL Server document that a CTE can refer only to CTEs defined earlier in the same clause. PostgreSQL relaxes that rule when the clause starts with `WITH RECURSIVE`, and SQLite accepts a forward reference, although a query that relies on it will not port. ## How Does WITH RECURSIVE Work? A recursive CTE has two parts joined by `UNION ALL` (or `UNION`). The anchor member runs once and produces the starting rows. The recursive member then runs again and again, and each pass reads only the rows produced by the pass before it. The engine stops when a pass produces no new rows. PostgreSQL's manual notes that the evaluation is iterative internally, even though the syntax is called recursive. The smallest useful example counts from 1 to 5: ```sql WITH RECURSIVE counter(n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM counter WHERE n < 5 ) SELECT n FROM counter; ``` | n | | 1 | | 2 | | 3 | | 4 | | 5 | Trace it the way the engine does, one worksheet line at a time: 1. The anchor `SELECT 1` writes the first line, a single row with `n = 1`. 2. The recursive member reads that row and produces `n = 2`, which becomes the input to the next pass. 3. Passes continue until the input row is `n = 5`. The `WHERE n < 5` filter then removes it, the pass produces nothing, and the recursion ends. The `WHERE` clause in the recursive member is the stop condition. Leave it out and the query has no natural end, which is the first pitfall covered later on this page. ## Walking a Hierarchy with a Recursive CTE Hierarchies are the classic reason to learn recursive CTEs. The `employees` table only records each person's direct manager, so "everyone under Ben" or "everyone above Finn" needs as many joins as there are levels, and the number of levels is not known in advance. ### Down the Tree: Everyone Below a Manager Start with Ben as the anchor, then repeatedly find people whose manager is someone already found. A `depth` column records how many levels below Ben each person sits: ```sql WITH RECURSIVE team AS ( SELECT id, name, 0 AS depth FROM employees WHERE name = 'Ben' UNION ALL SELECT e.id, e.name, t.depth + 1 FROM employees e JOIN team t ON e.manager_id = t.id ) SELECT name, depth FROM team ORDER BY depth, name; ``` | name | depth | | Ben | 0 | | Dan | 1 | | Erin | 1 | | Finn | 2 | The first pass after the anchor finds Dan and Erin, whose manager is Ben. The second pass finds Finn, whose manager is Dan. The third pass finds nobody who reports to Finn, so the query stops without needing an explicit limit. In this query the data itself supplies the stop condition, since the tree runs out of branches. That holds only while the data is a true tree, which the pitfalls section tests. ### Building a Path for Every Row A depth number says how far down someone sits, and a path says how they got there. This query starts from the person with no manager and carries a growing text path down each branch: ```sql WITH RECURSIVE chart AS ( SELECT id, name, 0 AS depth, CAST(name AS VARCHAR(200)) AS path FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.id, e.name, c.depth + 1, CAST(CONCAT(c.path, ' > ', e.name) AS VARCHAR(200)) FROM employees e JOIN chart c ON e.manager_id = c.id ) SELECT path, depth FROM chart ORDER BY path; ``` | path | depth | | Ava | 0 | | Ava > Ben | 1 | | Ava > Ben > Dan | 2 | | Ava > Ben > Dan > Finn | 3 | | Ava > Ben > Erin | 2 | | Ava > Chloe | 1 | | Ava > Chloe > Gia | 2 | | Ava > Chloe > Hugo | 2 | Sorting by the path lists each manager directly above their own reports, in the same order as an org chart drawn top to bottom. Two details in the query exist for portability: - `CONCAT()` joins the text in PostgreSQL, MySQL, SQL Server, and SQLite 3.44.0 or later. On older SQLite, use the `||` operator, which MySQL treats as logical OR by default. - The two `CAST`s give the path the same type in both members. SQLite would run without them, although MySQL and SQL Server would not, as the pitfalls section explains. - MySQL's `CAST` does not accept `VARCHAR` , so write `CHAR(200)` in both casts there. MySQL returns that as an unpadded variable-length string, whereas SQL Server would pad a `CHAR(200)` with spaces, so keep `VARCHAR` on SQL Server. ### Up the Tree: A Chain of Managers Reversing the join condition walks upward instead. Start at Finn, and on each pass find the person whose `id` matches the current row's `manager_id`: ```sql WITH RECURSIVE chain AS ( SELECT id, name, manager_id, 0 AS steps FROM employees WHERE name = 'Finn' UNION ALL SELECT e.id, e.name, e.manager_id, c.steps + 1 FROM employees e JOIN chain c ON e.id = c.manager_id ) SELECT name, steps FROM chain ORDER BY steps; ``` | name | steps | | Finn | 0 | | Dan | 1 | | Ben | 2 | | Ava | 3 | The walk stops at Ava because her `manager_id` is `NULL`, and a join on `NULL` matches nothing. The `ORDER BY steps` matters as well, since no engine guarantees the order of a result, recursive or otherwise, without one. ## Generating a Date Series to Fill Gaps The `orders` table has no rows for 3 March or 5 March. A plain `GROUP BY order_day` skips those days entirely, which leaves holes in a daily report or chart. A recursive CTE can generate every date in the range, and a `LEFT JOIN` then attaches the orders: ```sql WITH RECURSIVE days(cal_day) AS ( SELECT DATE('2026-03-01') UNION ALL SELECT DATE(cal_day, '+1 day') FROM days WHERE cal_day < '2026-03-06' ) SELECT d.cal_day, COALESCE(SUM(o.amount), 0) AS total FROM days d LEFT JOIN orders o ON o.order_day = d.cal_day GROUP BY d.cal_day ORDER BY d.cal_day; ``` | cal_day | total | | 2026-03-01 | 120 | | 2026-03-02 | 120 | | 2026-03-03 | 0 | | 2026-03-04 | 200 | | 2026-03-05 | 0 | | 2026-03-06 | 60 | The two missing days now appear with a total of 0, and 2 March correctly adds its two orders together. The recursive structure is portable, although date arithmetic is one of the least portable corners of SQL, so the anchor and the step change from engine to engine: | Engine | Anchor | Recursive Step | | SQLite | `DATE('2026-03-01')` | `DATE(cal_day, '+1 day')` | | PostgreSQL | `DATE '2026-03-01'` | `cal_day + 1` | | MySQL | `DATE '2026-03-01'` | `cal_day + INTERVAL 1 DAY` | | SQL Server | `CAST('2026-03-01' AS DATE)` | `DATEADD(day, 1, cal_day)` | SQL Server also needs the `RECURSIVE` keyword removed, and a range longer than 100 days needs `OPTION (MAXRECURSION ...)` on the outer query. PostgreSQL users can skip the recursion altogether with its built-in `generate_series()` function, which returns a series of numbers or timestamps that can be cast to dates. ## Are CTEs Materialized? What Each Engine Does Materializing a CTE means running it once and keeping the rows in a temporary result, which the engine reads wherever the name appears. The alternative is inlining, where the engine pastes the CTE's query into the main query and optimizes the whole thing together, the way it would treat a subquery. Each of the two choices wins in different queries. Materializing avoids repeating expensive work when a CTE is referenced several times, while inlining lets the optimizer push a filter from the main query down into the CTE, so it reads only the rows it needs. The four engines make that decision in different ways: - PostgreSQL inlines a non-recursive, side-effect-free CTE (a plain `SELECT` with no volatile functions) that the main query references once, and computes it once when it is referenced more than once. `AS MATERIALIZED` or `AS NOT MATERIALIZED` overrides that choice. Releases before PostgreSQL 12 always materialized, which is why older advice calls a CTE an "optimization fence". - MySQL handles CTEs the same way it handles derived tables: it merges them into the outer query when it can and materializes them otherwise. A materialized CTE is built once per query, a recursive CTE is always materialized, and the `MERGE` and `NO_MERGE` optimizer hints can steer each reference. - SQLite lets its query planner choose. Since 3.35.0, it materializes a CTE that is used more than once by default, and it accepts the PostgreSQL-style `MATERIALIZED` and `NOT MATERIALIZED` hints, which its documentation describes as non-binding. - SQL Server documents that CTE results are not materialized. Each reference to the CTE re-runs its query, and Microsoft's advice for a result needed in several places is a temporary table. The practical upshot is that a CTE is a readability tool first. Check the plan with `EXPLAIN` (or the execution plan in SQL Server) before assuming a CTE made a query faster or slower, and write the materialization hint only when the plan shows a problem. ## How Do PostgreSQL, MySQL, SQLite, and SQL Server Differ? Plain CTEs behave much the same in all four engines on current releases. The differences sit mostly in recursion: how it is declared, and what stops a runaway query: | Engine | CTEs From | RECURSIVE Keyword | Recursion Limit | | PostgreSQL | 8.4 | Required for recursion | No depth setting | | MySQL | 8.0 | Required for recursion | 1000 levels by default | | SQLite | 3.8.3 | Optional | No depth setting | | SQL Server | 2005 | Not part of the syntax | 100 levels by default | The details behind those cells, engine by engine: - PostgreSQL allows only one recursive self-reference per query, on the right-hand side of the `UNION`. It adds `SEARCH` and `CYCLE` clauses that compute a depth-first or breadth-first ordering column and detect cycles for you, and it allows data-modifying statements such as `DELETE ... RETURNING` inside a `WITH` clause. - MySQL sets its limit with the `cte_max_recursion_depth` system variable, and a query that recurses past it is stopped with an error. The recursive member cannot use aggregate functions, window functions, `GROUP BY`, `ORDER BY`, or `DISTINCT`, although current releases allow `LIMIT` there. - SQLite accepts a recursive CTE with or without `RECURSIVE`, for compatibility with SQL Server and Oracle. Its recursive member cannot use aggregate or window functions, and a `LIMIT` on it stops the recursion once enough rows exist. - SQL Server treats a CTE as recursive whenever it refers to its own name. The anchor and recursive members must be joined with `UNION ALL`, the recursive member cannot use `GROUP BY`, `DISTINCT`, `TOP`, or outer joins, and `OPTION (MAXRECURSION n)` accepts 0 to 32767, where 0 removes the limit. One more difference affects where a CTE can go. MySQL, PostgreSQL, and SQLite accept a `WITH` clause at the start of a subquery, while SQL Server does not allow a nested `WITH` inside a CTE's subquery, so there every CTE has to sit in the single top-level clause. For a wider comparison of the three open-source engines, see [MySQL vs PostgreSQL vs SQLite](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/). ## Common Pitfalls and Debugging Tips Most CTE bugs come from recursion, and they trace back to two misunderstandings: what makes the recursion stop, and which member decides the shape of the rows. The four problems below cover both, plus one SQL Server syntax trap. ### A Recursive CTE That Never Stops The team query from earlier stops because the tree runs out of branches. Bad data can turn a tree into a loop. This update makes Ava report to Finn, so the chain Ben, Dan, Finn, Ava leads straight back to Ben: ```sql UPDATE employees SET manager_id = 6 WHERE id = 1; ``` The original team query now runs until something external stops it. A depth guard in the recursive member makes it safe to run, and the output shows the loop plainly: ```sql WITH RECURSIVE team AS ( SELECT id, name, 0 AS depth FROM employees WHERE name = 'Ben' UNION ALL SELECT e.id, e.name, t.depth + 1 FROM employees e JOIN team t ON e.manager_id = t.id WHERE t.depth < 4 ) SELECT name, depth FROM team ORDER BY depth, name; ``` | name | depth | | Ben | 0 | | Dan | 1 | | Erin | 1 | | Finn | 2 | | Ava | 3 | | Ben | 4 | | Chloe | 4 | Ben appearing again at depth 4 is the signature of a cycle. Without the guard, SQLite and PostgreSQL keep going until the query is cancelled, times out, or runs out of resources. MySQL stops at 1000 levels and SQL Server at 100 by default, each with an error. There are three ways to fix it, depending on what the query needs: - Use `UNION` instead of `UNION ALL` when each row holds only identifying columns, such as an id and a name. `UNION` discards a row that matches an earlier row in every column, so the second visit to Ben produces nothing new and the recursion ends. This does not work in SQL Server, which requires `UNION ALL`. - Keep a depth guard such as `WHERE t.depth < 20` set well above the real maximum depth. It stops a loop, although the rows it returns will still contain repeats. - Track the path and skip any row whose id is already on it, or use PostgreSQL's `CYCLE` clause, which does the same bookkeeping for you. This is the `UNION` version, run against the same looping data. Every person appears once, and the query ends on its own: ```sql WITH RECURSIVE team AS ( SELECT id, name FROM employees WHERE name = 'Ben' UNION SELECT e.id, e.name FROM employees e JOIN team t ON e.manager_id = t.id ) SELECT name FROM team ORDER BY name; ``` It returns all eight names, Ava through Hugo, because the loop now connects Ben to the whole company. `UNION` discards a row only when every column matches an earlier row, so this fix works here because each row holds just an id and a name. Add a depth or path column and each repeat visit becomes a different row, which means `UNION` no longer stops the loop. For those queries, use the depth guard or check the path for ids already visited. ### Forgetting the RECURSIVE Keyword Remove `RECURSIVE` from the team query and SQLite still runs it and returns the same four rows, because SQLite does not enforce the keyword. That makes SQLite a poor judge of whether the query is portable. MySQL's manual gives `ERROR 1146 (42S02): Table 'cte_name' doesn't exist` as the likely result, where `cte_name` is the CTE's name. PostgreSQL also treats the self-reference as an unknown table and reports that the relation does not exist. The error names a table that the query appears to define two lines earlier, which is the clue. Add `RECURSIVE` once, directly after `WITH`, even when only one of several CTEs is recursive. SQL Server is the exception, since writing `RECURSIVE` there is a syntax error. ### The Anchor Decides the Column Types In the path query, the anchor row holds a short name such as "Ava", and the recursive member appends to it. The engines disagree about what that does to the column's type: - MySQL infers each column's type from the anchor alone. Without the `CAST`, the path column would take the width of `name`, and longer paths would be truncated or rejected with `ERROR 1406 (22001): Data too long for column` in strict mode. - SQL Server requires the anchor and recursive columns to have exactly the same data type, so it raises a type-mismatch error rather than truncating. - PostgreSQL also rejects a recursive column whose type differs from the anchor's, and its error hint suggests casting the anchor. - SQLite uses dynamic typing and accepts the query without any cast, which is why the missing cast is easy to overlook while developing there. The fix follows the same pattern everywhere. Cast the anchor column to a type wide enough for the longest value the recursion can build, and cast the recursive member to the same type. The path query earlier on this page does both, with `CHAR(200)` in place of `VARCHAR(200)` on MySQL. ### The Missing Semicolon in SQL Server In SQL Server, `WITH` is also used for table hints, so a CTE that follows another statement in the same batch needs that earlier statement to end with a semicolon. Without it, the batch fails with a syntax error near `WITH`: ```sql -- T-SQL: end the previous statement DECLARE @boss VARCHAR(20) = 'Ben'; WITH team AS ( SELECT id, name FROM employees WHERE name = @boss ) SELECT name FROM team; ``` Some codebases write `;WITH` as a defensive habit. Ending every statement with a semicolon is the cleaner fix, and Microsoft already lists statements without a terminating semicolon as a deprecated feature. ## Best Practices and Trade-offs A CTE sits beside three other ways of naming an intermediate result. This table shows where each one fits: | Approach | Lasts For | Best For | | Subquery | One use inside a statement | A short filter or a single lookup | | CTE | One statement | Step-by-step queries and recursion | | Temporary table | Usually the session | A result reused by several statements | | View | Until dropped | A query many people and programs share | A few habits keep CTE queries correct and readable: - Name each CTE for what it holds, such as `dept_stats` or `above_avg`, so the main query reads like a sentence. - Give every recursive CTE a stop condition you can point to, and add a depth or cycle guard whenever the data could contain a loop. - Cast anchor columns that the recursion widens, even when SQLite accepts the query without the cast. - Treat a CTE as a way to organize a query, and use `EXPLAIN` before claiming it changed performance, especially on SQL Server, where naming a CTE does not guarantee that its result is cached for repeated references. The [SQL indexes and query optimization guide](/programming/sql/indexes/query-optimization/) explains how to read that plan. Choosing among the four comes down to how long the result must live. For a short result used once, a plain subquery is still the simpler choice. When a query has two or more steps that depend on each other, I would write CTEs, because each step gets a name and the query reads in the order the work happens. When several statements need the same result, a temporary table fits better, because a CTE disappears when its statement ends. When many people and programs share the query, a view keeps one definition in one place. The trade-off with a CTE is that readability is all it guarantees. On SQL Server, for example, a CTE referenced three times can run its query three times. ## Conclusion A CTE is the labeled working line on the accountant's worksheet: it names an intermediate result so the final query can refer to it plainly. Several CTEs can be chained, each building on the ones above it, and the whole structure disappears when the statement ends. A recursive CTE is a worksheet line built from the line above it, which is what lets SQL walk a hierarchy of unknown depth or produce a run of dates. Learn the anchor, the recursive member, and the stop condition first. When a recursive query misbehaves, check the stop condition before anything else, then check the anchor's column types. ## Frequently Asked Questions ### What is the difference between a CTE and a subquery? A CTE is a subquery that has been given a name at the top of the statement. The main query then reads it like a table, it can be referenced more than once, and it can be recursive. A plain subquery sits inline where it is used and has no name to reuse. ### Is a CTE faster than a subquery? Neither form guarantees better performance on your data. An optimizer may inline a CTE or keep an intermediate result, depending on the engine and query. Referring to the CTE repeatedly can change that choice. Compare execution plans and measured results on your own data before claiming a CTE is faster. ### Can one CTE refer to another CTE? A CTE can read from any CTE defined earlier in the same WITH clause, which lets a query build up in named steps. Defining them in order is the portable habit, since MySQL and SQL Server reject a reference to a CTE defined later. ### What is the difference between a CTE and a temporary table? A CTE lasts for a single statement and needs no CREATE or DROP. A temporary table usually lasts until the session ends, can be indexed, and can be read by several statements. Reach for a temporary table when many later queries need the same intermediate result. ### How do you stop a recursive CTE from looping forever? Give the recursive member a condition that eventually returns no rows, such as a depth limit. For data that can contain cycles, track the visited ids and skip any already seen, or use UNION when each row holds only identifying columns, so repeats are discarded. MySQL and SQL Server also enforce recursion limits, 1000 and 100 levels by default. ### Does SQL Server support WITH RECURSIVE? SQL Server supports recursive CTEs, but its syntax has no RECURSIVE keyword. Write a plain WITH clause, and SQL Server treats the CTE as recursive when it refers to its own name. The anchor and recursive members must be joined with UNION ALL. ### Can you use a CTE in an UPDATE or DELETE statement? Yes, in all four engines covered here. PostgreSQL, MySQL, SQLite, and SQL Server all accept a WITH clause in front of UPDATE and DELETE. PostgreSQL goes further and allows INSERT, UPDATE, or DELETE statements inside the WITH clause itself. ## Self-Check 1. A query defines `WITH a AS (...), b AS (SELECT * FROM a)`. Can the main query read from both `a` and `b`? 2. What does `WITH RECURSIVE c(n) AS (SELECT 10 UNION ALL SELECT n - 3 FROM c WHERE n > 3) SELECT n FROM c;` return? 3. Which part of a recursive CTE runs only once: the anchor member or the recursive member? 4. A recursive org-chart query returns the same person at depth 2 and again at depth 6. What does that tell you about the data? 5. A path column built with `CONCAT` works in SQLite but fails in MySQL with "Data too long". What change to the anchor member fixes the error? 6. Which of the four engines on this page rejects the word `RECURSIVE`, and which one silently accepts a recursive CTE without it? ### Answers 1. The main query can read both, because every CTE in the `WITH` clause is visible to it, and `b` can read `a` because `a` is defined first. 2. It returns 10, 7, 4, and 1. The row with `n = 1` fails the `n > 3` filter, so the next pass produces nothing and the recursion stops. 3. The anchor member runs once. The recursive member runs repeatedly, each time on the rows produced by the previous pass, until a pass returns no rows. 4. The hierarchy contains a cycle, so following managers leads back to someone already visited. Add a depth guard, track the path, or switch to `UNION` if each row holds only identifying columns such as an id and a name. 5. Cast the anchor's path column to a wider type, such as `CAST(name AS CHAR(200))`, because MySQL takes the column type from the anchor alone. MySQL's `CAST` accepts `CHAR(200)` and returns an unpadded string. 6. SQL Server rejects `RECURSIVE`, because its syntax has no such keyword. SQLite accepts a recursive CTE with or without it. ## Next Steps CTEs build directly on joins and aggregation, and recursive CTEs are a regular topic in intermediate SQL interviews. The pages below pick up where this guide leaves off: - [SQL joins](/programming/sql/joins/), which the hierarchy examples on this page use to connect each pass to the table. - [SQL UNION and UNION ALL](/programming/sql/queries/union/), the operator that joins the anchor and recursive members. - [SQL window functions](/programming/sql/queries/window-functions/), whose top-N pattern ranks rows inside a CTE and filters them in the outer query. - [Common SQL interview questions](/programming/sql/interview-questions/), for more practice with the query patterns employers test. - [SQL indexes and query optimization](/programming/sql/indexes/query-optimization/), for reading the plans that show whether a CTE was inlined or materialized. - The engine guides for [PostgreSQL](/programming/sql/databases/postgresql/), [MySQL](/programming/sql/databases/mysql/), and [SQLite](/programming/sql/databases/sqlite/), or the [SQL guide](/programming/sql/) for the full topic list. --- ## SQL UNION and UNION ALL: Combine Compatible Result Sets URL: /programming/sql/queries/union/ Cluster: Programming > Sql `UNION` combines rows from separate [queries](/programming/sql/) into one result, and each column position must describe the same kind of value. Default to `UNION ALL`, which keeps every row, and use `UNION` only when removing identical rows is part of the requirement. A join does a different job by placing related columns beside each other. Use explicit column lists, and do not use a growing union to hide a schema problem. ## The Basic Shape Each query in the union must return compatible columns: ```sql SELECT email, created_at, 'customer' AS source FROM customers UNION SELECT email, created_at, 'subscriber' AS source FROM newsletter_subscribers; ``` The result has one column set: `email`, `created_at`, and `source`. PostgreSQL's docs put the compatibility rule plainly: the two queries have to return the same number of columns, and the column types need to be compatible. MySQL follows the same practical shape for everyday use. Make the columns explicit because `SELECT *` is lazy in a normal query and worse in a union, where one extra column can break the whole thing. ## UNION Removes Duplicates `UNION` removes duplicate rows from the final result: ```sql SELECT email FROM customers UNION SELECT email FROM newsletter_subscribers; ``` Suppose `customers` contains Alex and Sam, while `newsletter_subscribers` contains Sam and Taylor. The expected result has three rows. This order is shown for readability and is not guaranteed without `ORDER BY`: ```text email ------------------ alex@example.com sam@example.com taylor@example.com ``` Sam's identical row appears once. Duplicate removal changes the meaning of the result and requires extra work, so use it only when deduplication belongs to the requirement. ## UNION ALL Keeps Everything `UNION ALL` keeps every row from every branch: ```sql SELECT email FROM customers UNION ALL SELECT email FROM newsletter_subscribers; ``` With the same sample inputs, the expected result keeps both Sam rows: ```text email ------------------ alex@example.com sam@example.com sam@example.com taylor@example.com ``` Use `UNION ALL` when duplicates are meaningful or the inputs cannot overlap. Use `UNION` when the combined result must contain distinct selected rows. ## Order the Final Result An `ORDER BY` at the end sorts the combined result: ```sql SELECT title, published_at FROM articles UNION ALL SELECT title, published_at FROM archived_articles ORDER BY published_at DESC LIMIT 20; ``` That `ORDER BY` belongs to the final result, not only to the second query. If each input needs its own limit or sort before combining, put that query in a subquery and make the intent visible: ```sql SELECT id, title, published_at FROM ( SELECT id, title, published_at FROM articles ORDER BY published_at DESC, id DESC LIMIT 20 ) AS recent_live UNION ALL SELECT id, title, published_at FROM ( SELECT id, title, published_at FROM archived_articles ORDER BY published_at DESC, id DESC LIMIT 20 ) AS recent_archive ORDER BY published_at DESC, id DESC LIMIT 20; ``` That is more typing, but it makes each branch's trimming rule visible before the final merge. ## Use a Union for Same-shaped Results Good union use cases usually sound like this: - Current rows plus archived rows. - Customers plus newsletter-only contacts. - Internal events plus external events. - Search hits from two same-shaped sources. - Reports that need one list from several tables. Weak union use cases often sound like schema confusion: - Two tables that should be one table with a type column. - Repeated tables by month because partitioning felt too formal. - A report that glues unrelated facts together because the UI wants one grid. A union can keep a legacy report running, but repeated branches become difficult to own once the report turns into business logic. When every new record type needs another branch, inspect the schema and reporting model before extending the query. ## UNION Is Not a Join Use a join when one row needs data from related tables: ```sql SELECT orders.id, users.email FROM orders JOIN users ON users.id = orders.user_id; ``` Use a union when two queries produce the same column shape and the rows belong in one list: ```sql SELECT title, published_at FROM articles UNION ALL SELECT title, published_at FROM archived_articles; ``` If you are trying to put user columns next to order columns, read [SQL joins explained](/programming/sql/joins/). If you are stacking rows from separate sources, a union may be the right tool. ## PHP and Prepared Statements The same prepared-statement rule applies, and values belong in parameters: ```php prepare( "SELECT title, published_at, 'live' AS source FROM articles WHERE title LIKE :term_live UNION ALL SELECT title, published_at, 'archive' AS source FROM archived_articles WHERE title LIKE :term_archive ORDER BY published_at DESC LIMIT 20" ); $statement->execute([ "term_live" => "%" . $query . "%", "term_archive" => "%" . $query . "%", ]); ``` Do not parameterize table names or other SQL identifiers. The `PDO::prepare()` documentation notes that parameter markers represent complete data literals, not identifiers or arbitrary SQL fragments. If users can choose which tables enter the union, map their choice to a fixed allowlist in PHP before building the query. ## Common Pitfalls & Debugging ### The Branches Return Different Column Counts Symptom: the database rejects the union before returning rows. Cause: one branch added or removed a selected column. Fix: list every column explicitly and align each position by meaning, not only by type. ### Compatible Types Change the Value Symptom: numbers become text or dates lose the expected representation. Cause: the engine found a common output type across mismatched branch expressions. Fix: cast each branch deliberately and confirm the combined column type in the target database. ### A Branch Sort Is Mistaken for Final Order Symptom: the displayed rows move between executions. Cause: a sort inside one branch was assumed to order the combined result. Fix: apply `ORDER BY` after the final branch, using the output column name or supported position. ## Frequently Asked Questions ### Can UNION combine more than two SELECT queries? Yes. Add another UNION or UNION ALL operator between each compatible SELECT query. Every branch must return the same number of columns in a compatible order. Use parentheses when a branch needs its own limit or ordering. ### Which SELECT defines the output column names? The first SELECT normally supplies the column names for the combined result. Alias its expressions clearly because later aliases do not rename the final columns. Refer to those output names when applying the final ORDER BY. ### Can UNION combine results from the same table? Yes. The branches may read the same table with different filters, calculations, or labels. A single SELECT with OR, CASE, or a simpler predicate may be easier, so compare the shapes before keeping separate branches. ### When is UNION ALL faster than UNION? UNION ALL can be faster when duplicate removal is unnecessary because the database can append the branch results without a distinct step. The actual difference depends on row counts and the execution plan, so choose semantics first and measure important queries. ### Can you use UNION inside an INSERT statement? Yes. INSERT ... SELECT accepts a union as its source, so several selects can load one table in a single statement. The column count and types still have to line up, exactly as they do in a standalone union. ### Can a UNION combine tables from two different databases? Not within one plain query. Crossing databases needs a feature built for it, such as a federated or foreign table, a linked server, or attaching a second file in SQLite. Otherwise fetch each result separately and combine them in application code. ### Can you UNION rows that have no FROM clause? Yes, and it is a handy way to build a small reference list inline: select a literal value, union another, and treat the result as a table. It is useful for seeding lookup data or supplying a fixed set of options to a join. ## The Practical Rule Reach for `UNION ALL` when you want to stack compatible rows and keep them all. Reach for `UNION` when duplicate removal is part of the requirement. Reach for a join when the row needs related data from another table. And when a union keeps growing new branches, step back to [schema design basics](/programming/sql/schema-design/) before the query becomes the schema nobody meant to design. --- ## SQL Window Functions: ROW_NUMBER, RANK, PARTITION BY URL: /programming/sql/queries/window-functions/ Cluster: Programming > Sql A window function calculates a value from a set of related rows and writes the answer onto every one of those rows, instead of collapsing them into a single summary row. That one property lets a single query rank sales inside each region or keep a running total beside every sale, which plain aggregation cannot do without extra joins. A bank statement is the everyday version of the idea. Each transaction keeps its own line, and the balance column beside it is worked out from that line plus every line above it. A monthly summary replaces all of those lines with one total, which is what `GROUP BY` does. A window function is the balance column: the lines stay, and a calculated value appears next to each one. Where PostgreSQL, MySQL, or SQL Server behave differently from SQLite, the text says so. ## What Is a SQL Window Function? A window function is a function followed by an `OVER` clause. The `OVER` clause describes the window, meaning the set of rows the function is allowed to look at when it calculates the value for the current row. Each of the three terms below maps onto the bank statement: - Partition: a group of rows that is calculated separately, like a separate statement for each account. - Window order: the order of rows inside a partition, like the date order on the statement. - Frame: the slice of the partition used for the current row, like "this line and every line above it" for a running balance. Rows that share the same value in the window's `ORDER BY` are called peers. Peers matter for ties, and they come back in the ranking and frames sections. ### The Sample Table Every example uses this small sales table. It has two regions and a few deliberate ties, because ties are where window functions surprise people. ```sql CREATE TABLE sales ( id INTEGER PRIMARY KEY, region VARCHAR(10) NOT NULL, rep VARCHAR(20) NOT NULL, sale_day DATE NOT NULL, amount INTEGER NOT NULL ); INSERT INTO sales VALUES (1, 'North', 'Ana', '2026-03-02', 400), (2, 'North', 'Ben', '2026-03-02', 250), (3, 'North', 'Ana', '2026-03-03', 250), (4, 'North', 'Cara', '2026-03-05', 300), (5, 'South', 'Dev', '2026-03-02', 500), (6, 'South', 'Eli', '2026-03-04', 500), (7, 'South', 'Dev', '2026-03-04', 150), (8, 'South', 'Fay', '2026-03-06', 350); ``` The column types are plain enough for SQLite, PostgreSQL, MySQL, and SQL Server to accept the script unchanged. SQLite has no separate date type, so it stores `sale_day` as ISO text, which still sorts in date order. ### Window Functions Compared with GROUP BY Start with the monthly summary version of the question. `GROUP BY` returns one row per region, and the individual sales are gone from the result: ```sql SELECT region, SUM(amount) AS total FROM sales GROUP BY region; ``` | region | total | | North | 1200 | | South | 1500 | The window version keeps all eight sales and adds the total beside each one. That difference is the whole reason window functions exist, and the next section shows it. ## How Does the OVER() Clause Work? The general shape is a function, the keyword `OVER`, and up to three optional parts inside the parentheses: ```sql function_name(args) OVER ( [PARTITION BY expr, ...] [ORDER BY expr [ASC | DESC], ...] [frame_clause] ) ``` - `PARTITION BY` splits the rows into groups that are calculated independently. Leave it out and the whole result is one partition. - `ORDER BY` sets the order inside each partition. Ranking functions and `LAG`/`LEAD` need it to mean anything. - `frame_clause` narrows the rows used for the current row, such as `ROWS BETWEEN 2 PRECEDING AND CURRENT ROW`. The simplest possible window is an empty pair of parentheses. `OVER ()` means "every row in the result", so an aggregate returns the same grand total on every line: ```sql SELECT rep, amount, SUM(amount) OVER () AS all_sales FROM sales ORDER BY id; ``` | rep | amount | all_sales | | Ana | 400 | 2700 | | Ben | 250 | 2700 | | Ana | 250 | 2700 | | Cara | 300 | 2700 | | Dev | 500 | 2700 | | Eli | 500 | 2700 | | Dev | 150 | 2700 | | Fay | 350 | 2700 | Eight rows went in and eight rows came out. The `all_sales` column is the balance column from the analogy, calculated here over the entire table. ### PARTITION BY Splits the Rows into Groups Add `PARTITION BY region` and the sum restarts for each region. This is the separate statement for each account: ```sql SELECT region, rep, amount, SUM(amount) OVER ( PARTITION BY region ) AS region_total FROM sales ORDER BY id; ``` | region | rep | amount | region_total | | North | Ana | 400 | 1200 | | North | Ben | 250 | 1200 | | North | Ana | 250 | 1200 | | North | Cara | 300 | 1200 | | South | Dev | 500 | 1500 | | South | Eli | 500 | 1500 | | South | Dev | 150 | 1500 | | South | Fay | 350 | 1500 | The totals match the `GROUP BY` result above, 1200 and 1500. The difference is that each sale is still present, so a query can now compare a single sale with its region total in the same row. ### ORDER BY Inside the Window The `ORDER BY` inside `OVER()` is separate from the `ORDER BY` at the end of the query. The inner one decides how the window function sees the rows, and the outer one decides how the finished result is displayed. Adding an inner `ORDER BY` also changes what an aggregate such as `SUM` returns. Without it, the frame is the whole partition. With it, the default frame runs from the start of the partition to the current row and its peers, which turns `SUM` into a running total. The running-totals section below relies on exactly that behavior. ## ROW_NUMBER vs RANK vs DENSE_RANK: How Ties Change the Numbers The three ranking functions agree until two rows tie. The sample table has two ties, a pair of 500 sales and a pair of 250 sales, so ranking every sale by amount shows all three behaviors at once: ```sql SELECT amount, ROW_NUMBER() OVER ( ORDER BY amount DESC) AS row_num, RANK() OVER ( ORDER BY amount DESC) AS rnk, DENSE_RANK() OVER ( ORDER BY amount DESC) AS dense FROM sales ORDER BY amount DESC, row_num; ``` | amount | row_num | rnk | dense | | 500 | 1 | 1 | 1 | | 500 | 2 | 1 | 1 | | 400 | 3 | 3 | 2 | | 350 | 4 | 4 | 3 | | 300 | 5 | 5 | 4 | | 250 | 6 | 6 | 5 | | 250 | 7 | 6 | 5 | | 150 | 8 | 8 | 6 | Read the table one column at a time: - `ROW_NUMBER()` gives every row a different number, 1 to 8. The two 500 sales get 1 and 2, and which of them gets 1 is up to the engine. - `RANK()` gives tied rows the same number and then skips. Both 500 sales rank 1, and the 400 sale ranks 3 because two rows are ahead of it. - `DENSE_RANK()` gives tied rows the same number without skipping. The 400 sale ranks 2, because it holds the second-highest distinct amount. The choice follows the question being asked. `ROW_NUMBER` suits "exactly one row per slot", `RANK` suits competition-style placings, and `DENSE_RANK` suits "the second-highest distinct value", which is a common interview question. ## Running Totals with SUM() OVER A running total is the bank-statement balance in its literal form. Partition by region, order by date, and `SUM` adds each sale to everything before it in that region: ```sql SELECT region, sale_day, amount, SUM(amount) OVER ( PARTITION BY region ORDER BY sale_day, id ) AS running FROM sales ORDER BY region, sale_day, id; ``` | region | sale_day | amount | running | | North | 2026-03-02 | 400 | 400 | | North | 2026-03-02 | 250 | 650 | | North | 2026-03-03 | 250 | 900 | | North | 2026-03-05 | 300 | 1200 | | South | 2026-03-02 | 500 | 500 | | South | 2026-03-04 | 500 | 1000 | | South | 2026-03-04 | 150 | 1150 | | South | 2026-03-06 | 350 | 1500 | The last row of each region lands on the region total, 1200 and 1500, which is a quick way to check the query. The `id` in the window's `ORDER BY` is there for a reason: two North sales share a date, and without a tiebreaker the default frame treats them as peers. The frames section shows what that does to the numbers. ## Comparing Rows with LAG and LEAD `LAG` reads a value from an earlier row in the partition, and `LEAD` reads one from a later row. Before window functions, the same comparison usually meant joining a table to itself on "the previous row", which is awkward to write and easy to get wrong. For most of the years I ran MySQL on LAMP servers, that self-join was a common option, because MySQL did not add window functions until version 8.0 in 2018, and writing that self-join is one job I have been glad to give up. ```sql SELECT region, amount, LAG(amount) OVER ( PARTITION BY region ORDER BY sale_day, id ) AS prev_amt, LEAD(amount) OVER ( PARTITION BY region ORDER BY sale_day, id ) AS next_amt FROM sales ORDER BY region, sale_day, id; ``` | region | amount | prev_amt | next_amt | | North | 400 | NULL | 250 | | North | 250 | 400 | 250 | | North | 250 | 250 | 300 | | North | 300 | 250 | NULL | | South | 500 | NULL | 500 | | South | 500 | 500 | 150 | | South | 150 | 500 | 350 | | South | 350 | 150 | NULL | The first row of each region has no previous row, so `LAG` returns `NULL`, and the last row gets `NULL` from `LEAD`. The `sqlite3` shell prints `NULL` as an empty cell by default; the table above spells it out. Both functions take two optional arguments: how many rows to look back or ahead, and a default to use instead of `NULL`. A zero default makes the first row's change equal to its own amount, which is useful when the result feeds a chart: ```sql SELECT sale_day, amount, amount - LAG(amount, 1, 0) OVER ( ORDER BY sale_day, id ) AS change FROM sales WHERE region = 'North' ORDER BY sale_day, id; ``` | sale_day | amount | change | | 2026-03-02 | 400 | 400 | | 2026-03-02 | 250 | -150 | | 2026-03-03 | 250 | 0 | | 2026-03-05 | 300 | 50 | The `WHERE region = 'North'` filter runs before the window function, so the window only ever sees North rows. That ordering of clauses is also the cause of the most common window-function error, covered in the pitfalls section. ## Top-N per Group with ROW_NUMBER "The two biggest sales in each region" is a common first reason to learn window functions. Number the rows inside each region, then keep the rows numbered 1 and 2. The numbering has to happen in an inner query, because a window function cannot be filtered in the same query's `WHERE` clause: ```sql WITH ranked AS ( SELECT region, rep, amount, ROW_NUMBER() OVER ( PARTITION BY region ORDER BY amount DESC, id ) AS pos FROM sales ) SELECT region, rep, amount, pos FROM ranked WHERE pos <= 2 ORDER BY region, pos; ``` | region | rep | amount | pos | | North | Ana | 400 | 1 | | North | Cara | 300 | 2 | | South | Dev | 500 | 1 | | South | Eli | 500 | 2 | The `WITH ranked AS (...)` part is a [common table expression](/programming/sql/queries/common-table-expressions/), a named subquery that the outer query reads like a table. A plain subquery in the `FROM` clause works the same way if you prefer it. Look closely at the South rows in that result. Dev and Eli both sold 500, and `ROW_NUMBER` still had to put one of them first. The `id` tiebreaker makes that choice repeatable. When tied rows should all be kept, switch to `RANK`: ```sql WITH ranked AS ( SELECT region, rep, amount, RANK() OVER ( PARTITION BY region ORDER BY amount DESC ) AS pos FROM sales ) SELECT region, rep, amount FROM ranked WHERE pos = 1 ORDER BY region, rep; ``` | region | rep | amount | | North | Ana | 400 | | South | Dev | 500 | | South | Eli | 500 | "Top sale per region" now returns three rows, because South has a genuine tie for first place. My rule is to settle which answer the report needs before choosing the function, since both queries are correct for different questions. ## Window Frames: ROWS vs RANGE The frame is the part of the partition that an aggregate uses for the current row. `ROWS` counts physical rows, one at a time, back from the current row. `RANGE` works on the values in the window's `ORDER BY`, so rows with equal values (peers) enter or leave the frame together, unless an `EXCLUDE` option says otherwise. When a window has an `ORDER BY` and no frame clause, the default is `RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`. That default is the reason two sales on the same day can show the same running total. This query orders only by date, then compares the default frame with a `ROWS` frame: ```sql SELECT sale_day, amount, SUM(amount) OVER ( ORDER BY sale_day ) AS by_range, SUM(amount) OVER ( ORDER BY sale_day ROWS UNBOUNDED PRECEDING ) AS by_rows FROM sales WHERE region = 'North' ORDER BY sale_day, id; ``` | sale_day | amount | by_range | by_rows | | 2026-03-02 | 400 | 650 | 400 | | 2026-03-02 | 250 | 650 | 650 | | 2026-03-03 | 250 | 900 | 900 | | 2026-03-05 | 300 | 1200 | 1200 | Both 2 March sales show 650 under `by_range`, because the frame includes every peer of the current row. The `by_rows` column counts rows one at a time instead. However, the order of the two tied rows inside that frame is still the engine's choice, so another engine could put 250 before 400. Adding a unique tiebreaker such as `id` to the window's `ORDER BY` fixes both problems. Frames also make it possible to calculate moving values such as a rolling average. This query averages each South sale with up to two sales before it: ```sql SELECT sale_day, amount, ROUND(AVG(amount) OVER ( ORDER BY sale_day, id ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ), 1) AS avg_last_3 FROM sales WHERE region = 'South' ORDER BY sale_day, id; ``` | sale_day | amount | avg_last_3 | | 2026-03-02 | 500 | 500.0 | | 2026-03-04 | 500 | 500.0 | | 2026-03-04 | 150 | 383.3 | | 2026-03-06 | 350 | 333.3 | The first two rows average fewer than three values because there is nothing further back. SQL Server returns an integer when `AVG` is applied to an integer column, so there the argument needs a cast, such as `AVG(amount * 1.0)`, to keep the decimals. ## How Do PostgreSQL, MySQL, SQLite, and SQL Server Differ? The core functions on this page work in all four engines on any reasonably current release. The differences are mostly in which versions added support and how far frame syntax goes: | Engine | Window Functions From | Frame Limits | | PostgreSQL | 8.4 | ROWS, RANGE, GROUPS, EXCLUDE | | MySQL | 8.0 | ROWS and RANGE only | | SQLite | 3.25.0 | GROUPS and EXCLUDE from 3.28.0 | | SQL Server | 2012 for this page | RANGE without numeric offsets | The details behind those cells, engine by engine: - PostgreSQL added window functions in 8.4. Every currently supported release accepts `ROWS`, `RANGE`, and `GROUPS` frames, plus the `EXCLUDE` option. SQLite's developers used the PostgreSQL documentation as their primary reference and test against PostgreSQL, so the two rarely disagree. - MySQL added window functions in 8.0. Its parser recognizes `GROUPS` frames and `EXCLUDE` but raises an error for both, and it does not support `DISTINCT` inside an aggregate window function, as in `COUNT(DISTINCT rep) OVER (...)`. - SQLite added window functions in 3.25.0 and extended frames in 3.28.0. Python's `sqlite3` module uses whichever SQLite library it was built against, so check with `python3 -c "import sqlite3; print(sqlite3.sqlite_version)"` before relying on it. - SQL Server has had the ranking functions for longer, but frames, running totals with `ORDER BY`, and `LAG`/`LEAD` all need SQL Server 2012 or later. `ROW_NUMBER` requires an `ORDER BY` in its window, and `RANGE` frames accept only `UNBOUNDED` and `CURRENT ROW` boundaries. Two smaller differences change results rather than syntax. The first is `NULL` ordering: PostgreSQL sorts `NULL` after other values in ascending order, while MySQL, SQLite, and SQL Server sort it first, which moves `NULL` rows to the top or bottom of a ranking. PostgreSQL and SQLite accept `NULLS FIRST` and `NULLS LAST` to make that explicit. The second is the named `WINDOW` clause, which defines a window once and reuses it by name, as in `RANK() OVER w ... WINDOW w AS (ORDER BY amount DESC)`. PostgreSQL, MySQL, and SQLite all support the named `WINDOW` clause. SQL Server supports it from SQL Server 2022, and only when the database compatibility level is 160 or higher. The examples on this page repeat the window inline so they run on older SQL Server releases and lower compatibility levels too. For a wider comparison of the three open-source engines, see [MySQL vs PostgreSQL vs SQLite](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/). ## Common Pitfalls and Debugging Tips Many window-function bugs trace back to one of two misunderstandings: when the window is calculated relative to the rest of the query, and what the default frame contains. The errors below follow from one or the other. ### Filtering on a Window Function in WHERE Filtering on a window function in the same query fails, because `WHERE` runs before any window function has been calculated: ```sql SELECT region, rep, amount FROM sales WHERE ROW_NUMBER() OVER ( PARTITION BY region ORDER BY amount DESC ) = 1; ``` SQLite reports `misuse of window function ROW_NUMBER()`, and PostgreSQL reports `window functions are not allowed in WHERE`. Moving the window into the `SELECT` list and filtering on its alias fails too: SQLite reports `misuse of aliased window function pos`, and PostgreSQL reports `column "pos" does not exist`, which is the more confusing message of the two. The fix in every engine is the pattern from the top-N section. Calculate the window in a subquery or common table expression, then filter on its alias in the outer query. The same restriction applies to `GROUP BY` and `HAVING`, since window functions are only allowed in the `SELECT` list and the final `ORDER BY`. ### LAST_VALUE Returns the Current Row `FIRST_VALUE` behaves as expected with the default frame, because the frame always starts at the first row. `LAST_VALUE` does not, because the default frame ends at the current row, or at its last peer when the window's `ORDER BY` has ties: ```sql SELECT sale_day, amount, LAST_VALUE(amount) OVER ( ORDER BY sale_day, id ) AS last_amt FROM sales WHERE region = 'North' ORDER BY sale_day, id; ``` | sale_day | amount | last_amt | | 2026-03-02 | 400 | 400 | | 2026-03-02 | 250 | 250 | | 2026-03-03 | 250 | 250 | | 2026-03-05 | 300 | 300 | Each row reports its own amount, which looks like the function is broken. The query ran correctly and the frame was simply too short. Extend the frame to the end of the partition and every row reports 300, the last North sale: ```sql SELECT sale_day, amount, LAST_VALUE(amount) OVER ( ORDER BY sale_day, id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS last_amt FROM sales WHERE region = 'North' ORDER BY sale_day, id; ``` An alternative that needs no frame at all is `FIRST_VALUE(amount) OVER (ORDER BY sale_day DESC, id DESC)`, which reverses the order and takes the first row. ### Mixing GROUP BY with a Window Function When a query has both `GROUP BY` and a window function, the grouping happens first and the window runs over the grouped rows. A window that refers to an ungrouped column is therefore an error in PostgreSQL, SQL Server, and MySQL's default `ONLY_FULL_GROUP_BY` mode: ```sql SELECT region, SUM(amount) OVER () AS grand FROM sales GROUP BY region; ``` PostgreSQL refuses to run this query and reports the error `column "sales.amount" must appear in the GROUP BY clause or be used in an aggregate function`. SQLite accepts it, because SQLite allows ungrouped "bare" columns and takes their value from an arbitrary row in each group. On the sample table it can return 900 on both rows, a number that matches neither region's total nor the grand total of 2700. I would rather have PostgreSQL's error, because a query that runs and returns a wrong number deserves more suspicion than one that fails. The correct form wraps the grouped aggregate inside the window aggregate. The inner `SUM` produces one total per region, and the outer `SUM ... OVER ()` adds those totals up: ```sql SELECT region, SUM(amount) AS total, SUM(SUM(amount)) OVER () AS grand, ROUND(100.0 * SUM(amount) / SUM(SUM(amount)) OVER (), 1) AS pct FROM sales GROUP BY region ORDER BY region; ``` | region | total | grand | pct | | North | 1200 | 2700 | 44.4 | | South | 1500 | 2700 | 55.6 | The `100.0` literal in that query matters as well. Dividing one integer by another truncates the result in SQLite, PostgreSQL, and SQL Server, and the decimal literal forces a decimal division. ### ROW_NUMBER Without a Tiebreaker When the window's `ORDER BY` has ties, `ROW_NUMBER` still has to number the tied rows, and the order it picks is not guaranteed. The query can return different numbers after an index change, an engine upgrade, or a different execution plan, with no error at all. The symptom is a report that changes between runs even though the data did not. Add a unique column, usually the primary key, as the last item in the window's `ORDER BY`. Pagination code built on `ROW_NUMBER` needs the same fix, or rows can repeat or vanish between pages. ## Best Practices and Trade-offs Window functions sit beside the tools a reader probably knows already. This table shows where each one fits: | Approach | Keeps Each Row | Best For | | GROUP BY | No, one row per group | Totals and counts per group | | Window function | Yes | Ranks, running totals, row comparisons | | Self-join or subquery | Yes | Engines without window support | A few habits keep window queries correct and readable: - End every window `ORDER BY` with a unique column whenever the order must be repeatable. - Write the frame out explicitly for `LAST_VALUE`, moving averages, and any running total over data that can tie. - Filter on a window result in an outer query, and give the inner query a name that says what it ranks. - Check the plan with `EXPLAIN` on large tables. Each distinct window usually means a sort, and an index that matches the `PARTITION BY` and `ORDER BY` columns, such as `(region, sale_day)`, can let the planner avoid one. The [SQL indexes and query optimization guide](/programming/sql/indexes/query-optimization/) explains how to read that plan. For totals that genuinely need one row per group, `GROUP BY` is still the simpler and clearer query. ## Conclusion A window function is the balance column on the bank statement: every row stays in the result, and a value calculated from its neighbors sits beside it. `PARTITION BY` decides which statement a row belongs to, the window's `ORDER BY` decides the order of the lines, and the frame decides how far back the calculation looks. For most reporting work, I would learn `ROW_NUMBER`, `RANK`, `SUM() OVER`, and `LAG` first, and add a tiebreaker whenever the order has to be repeatable. The default frame and the "filter in an outer query" rule sit behind every pitfall on this page, so check those two first when a result looks wrong. ## Frequently Asked Questions ### What is the difference between a window function and GROUP BY? GROUP BY collapses each group into a single output row, so the individual rows disappear from the result. A window function calculates across the same kind of group but keeps every input row, adding the calculated value as an extra column beside the original data. ### Can you use a window function in a WHERE clause? Not directly, because window functions are calculated after WHERE, GROUP BY, and HAVING have already run, so the value does not exist yet when WHERE filters rows. Calculate the window function in a subquery or common table expression, then filter on its alias in the outer query. ### What happens when OVER() has no PARTITION BY clause? The whole result set becomes one partition. With an empty OVER (), every row sees every row, so SUM(amount) OVER () returns the grand total on each row. With only an ORDER BY, ROW_NUMBER() numbers the entire result from 1 upward without restarting. ### When should you use DENSE_RANK instead of RANK? Use DENSE_RANK when you want to ask for the top distinct values, such as the three highest amounts, regardless of how many rows share each value. RANK leaves gaps after ties, which matches how competitions usually number places after a shared result. ### What does LAG return when there is no previous row? LAG returns NULL for the first row of each partition, because there is nothing before it. Pass a third argument to supply a default instead, as in LAG(amount, 1, 0), which returns 0 on the first row. LEAD behaves the same way at the end of a partition. ### Does every database support SQL window functions? The major relational engines do on any reasonably current release. MySQL needs version 8.0 or later, SQLite needs 3.25.0 or later, SQL Server has the running-total and LAG features from 2012, and PostgreSQL has supported window functions since 8.4. Frame options vary more than the functions do. ### Are window functions slow on large tables? They usually cost a sort for each distinct PARTITION BY and ORDER BY combination in the query. On a large table that sort is the main expense. An index that matches those columns can let the planner skip it, so check the plan with EXPLAIN before rewriting anything. ## Self-Check 1. Three rows have amounts 90, 90, and 80, ordered by amount descending. What does `RANK()` return for the 80 row, and what does `DENSE_RANK()` return? 2. What does `SUM(amount) OVER ()` return on each row of the sample table? 3. Why does `WHERE ROW_NUMBER() OVER (ORDER BY amount) = 1` fail? 4. A running total ordered only by `sale_day` shows the same value on two rows. What causes that, and how do you fix it? 5. Which function would you use to show each sale next to the previous sale in the same region? 6. Why does `LAST_VALUE(amount) OVER (ORDER BY sale_day, id)` return each row's own amount? ### Answers 1. `RANK()` returns 3 for the 80 row, because two rows rank ahead of it and the rank skips 2. `DENSE_RANK()` returns 2, because it never leaves gaps after a tie. 2. It returns 2700 on every row, the grand total of all eight sales, because an empty window treats the whole result as one partition. 3. The `WHERE` clause runs before window functions are calculated, so the value does not exist yet. Calculate it in a subquery or CTE and filter in the outer query. 4. The two rows share a date, so they are peers, and the default `RANGE` frame includes both of them. Add a unique tiebreaker such as `id` to the window's `ORDER BY`, or use a `ROWS` frame with that tiebreaker. 5. Use `LAG(amount) OVER (PARTITION BY region ORDER BY sale_day, id)`, which returns `NULL` on the first sale of each region. 6. The default frame ends at the current row, and this window's `ORDER BY` is unique, so the last row of the frame is the current row. Extend the frame with `ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING`. ## Next Steps Window functions build directly on joins and aggregation, and they are a regular topic in intermediate SQL interviews. The pages below pick up where this guide leaves off: - [Common SQL interview questions](/programming/sql/interview-questions/), including the "second-highest salary" question that `DENSE_RANK` answers cleanly. - [SQL joins](/programming/sql/joins/), the older way to compare rows with related rows, and still the right tool for combining tables. - [SQL UNION and UNION ALL](/programming/sql/queries/union/), the other query technique in this section, for stacking result sets. - [MySQL vs PostgreSQL vs SQLite](/programming/sql/comparisons/mysql-vs-postgresql-vs-sqlite/), for the engine differences beyond window functions. - The engine guides for [PostgreSQL](/programming/sql/databases/postgresql/), [MySQL](/programming/sql/databases/mysql/), and [SQLite](/programming/sql/databases/sqlite/), or the [SQL guide](/programming/sql/) for the full topic list. --- ## SQL Schema Design: Keys, Constraints, Indexes, Migrations URL: /programming/sql/schema-design/ Cluster: Programming > Sql A [database schema](/programming/sql/) is the blueprint for the facts an application keeps. Screens and controllers can be rebuilt, but stored relationships, identifiers, and constraints remain after those interfaces change. Good schema design gives each fact one clear home, rejects impossible states, and supports the queries the application must run. The blueprint should be strict where the business rule is strict and flexible only where absence or variation has a defined meaning. ## Start with Facts Describe the domain in short factual statements before creating tables: - A customer can place many orders. - An order belongs to one customer. - An order contains one or more products. - A product can appear in many orders. - Each order item records a positive quantity. These statements reveal entities, relationships, cardinality, and required values. A screen called "checkout" does not need a `checkout` table. The underlying customer, order, product, and item facts need tables that remain meaningful after the screen changes. ## A Worked Normalization Example Suppose an imported sales sheet stores one order with lists inside text fields: | Order | Customer | Products | | 1001 | sam@example.com | KB-1, MS-2 | A matching `quantities` value of `1,2` depends on list position. The database cannot enforce that each SKU exists, index one product cleanly, or join an order item without parsing text. ### Step 1: Make Each Value Atomic First normal form gives each row and column a single value. Expand the product list into one row per order and product pair: ```sql CREATE TABLE order_lines_raw ( order_id bigint NOT NULL, ordered_at timestamptz NOT NULL, customer_email text NOT NULL, customer_name text NOT NULL, product_sku text NOT NULL, product_name text NOT NULL, quantity integer NOT NULL, PRIMARY KEY (order_id, product_sku) ); ``` Order 1001 now produces two rows: `KB-1` with quantity 1 and `MS-2` with quantity 2. Individual products can be filtered and counted, but order and customer details repeat on both rows. ### Step 2: Separate Partial Dependencies The raw table has a composite key of `(order_id, product_sku)`. Some columns depend on only part of that key: `ordered_at` depends on the order, while `product_name` depends on the product SKU. Second normal form moves those facts to their owners. ```sql CREATE TABLE products ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, sku text NOT NULL UNIQUE, name text NOT NULL ); CREATE TABLE orders_stage ( id bigint PRIMARY KEY, ordered_at timestamptz NOT NULL, customer_email text NOT NULL, customer_name text NOT NULL ); CREATE TABLE order_items_stage ( order_id bigint NOT NULL, product_id bigint NOT NULL REFERENCES products (id), quantity integer NOT NULL CHECK (quantity > 0), PRIMARY KEY (order_id, product_id) ); ``` The product name is now stored once, and the order item contains only facts about the relationship: which product, which order, and how many. ### Step 3: Separate Transitive Dependencies In `orders_stage`, the customer name depends on the customer email, not directly on the order ID. Third normal form moves the customer facts into a customer table and gives the order a foreign key. The final `orders` and `order_items` tables replace the staging tables. ```sql CREATE TABLE customers ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, email text NOT NULL UNIQUE, name text NOT NULL ); CREATE TABLE orders ( id bigint PRIMARY KEY, customer_id bigint NOT NULL REFERENCES customers (id), ordered_at timestamptz NOT NULL ); CREATE TABLE order_items ( order_id bigint NOT NULL REFERENCES orders (id), product_id bigint NOT NULL REFERENCES products (id), quantity integer NOT NULL CHECK (quantity > 0), PRIMARY KEY (order_id, product_id) ); ``` Migrate the staged rows into the final customer, order, and order-item tables, then remove the staging tables after verification. The final blueprint has one owner for each customer, product, order, and order-item fact. ### Verify the Normalized Result A join should reconstruct the useful order view without parsing lists: ```sql SELECT orders.id AS order_id, customers.email, products.name AS product_name, order_items.quantity FROM orders JOIN customers ON customers.id = orders.customer_id JOIN order_items ON order_items.order_id = orders.id JOIN products ON products.id = order_items.product_id WHERE orders.id = 1001 ORDER BY products.sku; ``` For the sample data, the expected result has two rows: | Order | Product | Quantity | | 1001 | Keyboard | 1 | | 1001 | Mouse | 2 | The normalized tables preserve the original order while allowing the database to enforce every relationship and quantity. ## Choose Stable Keys A primary key gives a row stable identity. Generated identity columns or UUIDs work well for entities whose business labels may change. A customer's email, a product SKU, or an article slug can remain unique without becoming the internal relationship key. ```sql CREATE TABLE articles ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, slug text NOT NULL UNIQUE, title text NOT NULL ); ``` The generated identity gives foreign keys a stable anchor. The `slug` remains a protected public identifier that can follow its own rename and redirect policy. Composite keys fit relationship tables when the pair itself is the identity. The `(order_id, product_id)` key prevents the same product appearing twice in one order. If repeated lines are valid, add a separate line ID and encode the real uniqueness rule instead. ## Enforce Relationships and Values Foreign keys keep references valid across every writer. `NOT NULL` marks required values, `UNIQUE` protects identifiers, and `CHECK` rejects values outside a rule. ```sql CREATE TABLE coupons ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, code text NOT NULL UNIQUE, discount_percent integer NOT NULL CHECK (discount_percent BETWEEN 1 AND 100) ); ``` The expected result is that `discount_percent = 20` succeeds while `discount_percent = 140` fails. The rule stays with the data and applies to every writer. Use a composite unique constraint when uniqueness has a scope. `UNIQUE (tenant_id, email)` permits the same email in different tenants but prevents duplicates inside one tenant. ## Model NULL and Delete Behavior Use `NULL` only when absence has a defined meaning. A nullable `shipped_at` can mean the order has not shipped. A nullable customer ID on an order may mean the ownership rule is missing. Foreign keys also need deliberate delete behavior. `ON DELETE CASCADE` fits a child that cannot exist without its parent, such as an order item. Independent business records usually need `RESTRICT`, archival, or a status change instead of silent deletion. ```sql CREATE TABLE order_items ( order_id bigint NOT NULL REFERENCES orders (id) ON DELETE CASCADE, product_id bigint NOT NULL REFERENCES products (id) ON DELETE RESTRICT, quantity integer NOT NULL CHECK (quantity > 0), PRIMARY KEY (order_id, product_id) ); ``` Soft deletion adds lifecycle rules rather than removing them. Define visibility, uniqueness, restoration, child behavior, and final cleanup before adding a nullable `deleted_at` column. ## Index Real Access Paths Primary and unique keys have supporting indexes. Foreign-key columns on the referencing side may need separate indexes for joins, filters, parent updates, or deletes. ```sql CREATE INDEX orders_customer_id_idx ON orders (customer_id); CREATE INDEX orders_customer_date_idx ON orders (customer_id, ordered_at DESC); ``` The first index supports common customer-to-order joins efficiently. The second may support a route that lists one customer's newest orders. Confirm the choice with the real query and `EXPLAIN`, because every additional index increases storage and write work. ## Plan Schema Migrations A correct final blueprint still needs a safe construction sequence. Adding a required column to populated data commonly uses four phases: 1. Add the column as nullable or with a safe transitional default. 2. Backfill existing rows and verify the result. 3. Add the required constraint after every row is valid. 4. Remove transitional code after all writers use the new shape. Before adding a foreign key, find orphaned values. Find and resolve duplicates before adding a uniqueness constraint. Before splitting a table, compare row counts and reconstructed query results. Run the migration on production-shaped data and record rollback or roll-forward steps. ## Common Pitfalls and Debugging ### Storing Lists in Text Columns Comma-separated IDs cannot receive normal foreign keys and produce fragile substring queries. Move each relationship into a row in a join table, migrate the values, and compare counts before removing the text column. ### Adding a Foreign Key to Dirty Data The constraint fails when child rows reference missing parents. Find orphans with a left join, decide whether to repair, archive, or delete them, and add the constraint only after the cleanup result is reviewed. ### Normalizing Without Reading the Query A theoretically tidy model can still miss the application's main access path. Write the important reads beside the facts, then add suitable keys and indexes. Denormalize only after measurement identifies a real bottleneck and a refresh rule is defined. ### Using Soft Deletes Without Lifecycle Rules Rows reappear in queries or block unique values when filters and reuse rules are inconsistent. Centralize the active-row condition, decide whether deleted identifiers can be reused, and test restoration with related records. ## A Schema Review Checklist - Does each table own one kind of fact? - Does every row have a stable primary key? - Are business identifiers protected by appropriate unique constraints? - Do foreign keys express required relationships and delete rules? - Does each nullable column have a defined absent state? - Do checks reject invalid quantities, states, and ranges? - Do indexes follow measured joins, filters, and ordering? - Can migrations preserve and verify existing data? - Can the important view be reconstructed with expected results? ## Frequently Asked Questions ### What is database normalization? Normalization organizes facts so each value has a clear owner and unnecessary duplication is removed. First normal form keeps column values atomic and singular. Second normal form removes dependencies on part of a composite key. Third normal form removes dependencies between non-key attributes. ### Should every table have a generated ID? Most entity tables benefit from a stable generated key, but relationship tables can use a meaningful composite primary key. Keep changeable business identifiers such as email, SKU, or slug under unique constraints instead of making every foreign key depend on them. ### Should every foreign key have an index? No automatic rule covers every foreign-key index. Index a foreign-key column when joins, parent deletes, updates, or common filters need it. Check the actual query plans and write cost. The referenced primary or unique key is indexed, but the referencing column may need a separate index. ### When is denormalization reasonable? Denormalization is reasonable when a measured read path needs a derived value or historical snapshot and the update rule is explicit. Keep a canonical source, document how the copy is refreshed, and test for drift. Do not duplicate data only to avoid learning joins. ### What is the difference between a unique constraint and a unique index? A unique constraint is a rule about the data; a unique index is the structure most engines build to enforce it. In practice they usually arrive together, but the constraint is what other tools read as intent, so declare that. ### How do you rename a column without breaking a live application? Expand then contract. Add the new column, write to both, backfill the old values, switch reads to the new one, and only then drop the old column. A direct rename breaks whichever deployed version is still using the old name. ### Should every table have created_at and updated_at columns? For anything you will later debug or report on, yes. They cost almost nothing and answer questions no other data can once something has gone wrong. Pure join tables and static lookup tables are reasonable exceptions. ## Self-Check 1. What does this statement do against the coupon table's `CHECK` constraint? ```sql INSERT INTO coupons (code, discount_percent) VALUES ('TOO-MUCH', 140); ``` A. Inserts a 100 percent discount. B. Inserts 140 unchanged. C. Fails because 140 is outside the allowed range. Answer: C. The `CHECK` constraint accepts values from 1 through 100, so the database rejects this row. 2. Why does `product_name` move from the raw order-line table to `products`? A. It depends on the product SKU. B. It depends on the order date. C. It depends on the quantity. Answer: A. The name describes the product rather than the complete order-and-product relationship. 3. What should happen before adding a foreign key to existing rows? Find and resolve child values that do not match a parent row, then verify the cleanup. 4. Why keep a generated key beside a unique email address? The generated key remains stable when the business identifier changes, while the unique constraint still prevents duplicate emails. 5. When is a composite index on customer and date justified? When an important query filters by customer and orders the matching rows by date, and its query plan confirms the index helps. ## What to Read Next Continue with [SQL joins](/programming/sql/joins/) to query the relationships, then use [SQL indexes and query optimization](/programming/sql/indexes/query-optimization/) to inspect access paths. Read [SQL transactions and ACID](/programming/sql/transactions/acid/) before implementing multi-table writes. --- ## Storing Images in a Database URL: /programming/sql/storage/storing-images/ Cluster: Programming > Sql Ordinary product photos and article images often belong in external storage, with [SQL](/programming/sql/) holding their metadata. Small signatures, generated thumbnails, or controlled attachments can justify BLOB storage when their transaction and recovery requirements outweigh separate delivery. Either way, the database records ownership, format, location, and status, and that record must still agree with the bytes after uploads, replacements, backups, and failures. ## How to Choose the Storage Model Choose database BLOBs or external file storage against the same criteria: - Atomicity: must the bytes commit or roll back with one database row? - Size: how large is one image, and how quickly will the complete collection grow? - Read path: does the application stream individual images or repeatedly fetch them beside ordinary rows? - Delivery: does the image need direct file serving, caching, range requests, or a media-processing pipeline? - Recovery: can the chosen backup and restore process recover matching metadata and bytes? - Access: which system decides whether an image is public, private, or restricted? Database storage can simplify atomic row-plus-file changes and one-system backups. It also enlarges the database, backup set, restore job, and replication stream. External storage keeps ordinary queries and database backups smaller, but introduces cross-system consistency and cleanup work. ## How to Store Image Metadata A metadata row gives each image one reviewable record: ```sql CREATE TABLE product_images ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, product_id BIGINT NOT NULL, object_key TEXT NOT NULL UNIQUE, original_filename TEXT, mime_type TEXT NOT NULL, byte_size BIGINT NOT NULL CHECK (byte_size > 0), width INT NOT NULL CHECK (width > 0), height INT NOT NULL CHECK (height > 0), checksum_sha256 CHAR(64), processing_state VARCHAR(20) NOT NULL DEFAULT 'pending', visibility VARCHAR(20) NOT NULL DEFAULT 'private', created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at TIMESTAMP ); CREATE INDEX product_images_product_created_idx ON product_images (product_id, created_at DESC); ``` This example uses PostgreSQL-compatible identity and check syntax. A MySQL version would use `AUTO_INCREMENT`, InnoDB, and equivalent constraints. The storage model stays the same. A generated key locates the bytes, while typed columns describe the facts needed by application queries. Do not store a temporary signed URL as the durable location. Store the internal object key and generate a public or signed URL when serving the image. URLs can expire or change domains without changing image ownership. ## How to Record an Upload from PHP The upload handler should validate the file, generate its storage key, write the bytes, and record metadata through a prepared statement. The storage client is left outside this example because the boundary matters more than one provider library. ```php $maxBytes) { throw new RuntimeException('Image size is not allowed'); } $finfo = new finfo(FILEINFO_MIME_TYPE); $mimeType = $finfo->file($file['tmp_name']); $allowedTypes = ['image/jpeg', 'image/png', 'image/webp']; if ($mimeType === false || !in_array($mimeType, $allowedTypes, true)) { throw new RuntimeException('Image type is not allowed'); } $dimensions = getimagesize($file['tmp_name']); if ($dimensions === false) { throw new RuntimeException('Image dimensions are unavailable'); } [$width, $height] = $dimensions; $objectKey = sprintf( 'products/%d/images/%s', $productId, bin2hex(random_bytes(16)) ); // Write the validated bytes through the storage service here. $statement = $pdo->prepare( 'INSERT INTO product_images (product_id, object_key, original_filename, mime_type, byte_size, width, height) VALUES (:product_id, :object_key, :original_filename, :mime_type, :byte_size, :width, :height)' ); $statement->execute([ 'product_id' => $productId, 'object_key' => $objectKey, 'original_filename' => basename($file['name']), 'mime_type' => $mimeType, 'byte_size' => $file['size'], 'width' => $width, 'height' => $height, ]); ``` The server generates the object key and treats the original filename as untrusted display metadata. Fileinfo performs the MIME check, while `getimagesize()` reads dimensions only because PHP warns that a non-image file can pass that function. The database insert uses parameters, while the storage write remains an explicit step with its own failure path. ## When Database BLOBs Fit MySQL 9.7 defines four BLOB types with different maximum lengths. PostgreSQL 18.4 provides `bytea` for variable-length binary strings. These types make small binary values ordinary row data, which can be useful when one database transaction must protect both metadata and bytes. Set an application limit below the type's theoretical maximum: ```sql CREATE TABLE image_blobs ( id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, product_id BIGINT NOT NULL, mime_type VARCHAR(80) NOT NULL, byte_size BIGINT NOT NULL CHECK (byte_size > 0 AND byte_size <= 1048576), checksum_sha256 CHAR(64) NOT NULL, image_bytes BYTEA NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); ``` The one-megabyte rule is an example application limit. Choose the actual limit from upload requirements, memory tests, backup growth, and response behavior. MySQL would use the appropriate BLOB family type and enforce the same application rule. A BLOB design should also keep metadata queries separate from byte reads. Listing screens need IDs, dimensions, states, and perhaps a serving route. They do not need every binary payload in the result. ## How Images Affect Backups and Memory Image bytes inside SQL increase logical dumps, physical backups, restore time, replica traffic, storage snapshots, and disaster-recovery transfer. A backup that once held only compact relational rows can grow with every original upload and generated variant. That growth can be acceptable when the recovery objective allows it. Measure backup duration, retained size, restore duration, and replica lag with production-shaped image volume before selecting BLOB storage. Memory on read is the caveat many short comparisons omit. MySQL documents that a buffer used with a BLOB column grows dynamically and can become as large as the largest BLOB read during a scan. Its BLOB documentation also warns that result size is constrained by available memory and communication buffers. Application drivers can allocate their own buffers as values cross the connection. Exclude image bytes from ordinary BLOB table queries. Fetch metadata for lists, then retrieve or stream one image by ID on the serving path: ```sql SELECT id, product_id, mime_type, byte_size, created_at FROM image_blobs WHERE product_id = 4821 ORDER BY created_at DESC; SELECT mime_type, byte_size, image_bytes FROM image_blobs WHERE id = 917; ``` External storage has a different recovery cost. Database backups remain smaller, but recovery must prove that every restored row still points to an available object and that object retention matches database retention. ## How to Delete and Replace Images SQL and external storage do not share one atomic transaction. Replacement code therefore needs an order of operations and a repair path. 1. Validate and write the replacement under a new generated key. 2. Insert the new metadata row or update the current-image reference in SQL. 3. Mark the old row inactive or set `deleted_at`. 4. Serve the replacement only after processing succeeds. 5. Queue deletion of the old object after a retention period. 6. Retry failed cleanup and sweep for unreferenced objects. The new object reaches storage before the database points to it. If the SQL write fails, an orphan sweep can remove the unused object. Deleting the old object first creates the harder failure because the database can lose its only valid image before the replacement is ready. Database BLOBs can update row metadata and bytes inside one transaction, but versioning and retention still need explicit rules if older images must remain recoverable. ## How to Protect Private Images Authorization does not follow automatically from storage location. A BLOB query and an external-object request both need an application rule that checks ownership and visibility before returning bytes. Keep private external objects outside a public bucket path and serve them through an authorized route or short-lived signed URL. Keep database BLOB reads behind a query that applies the same ownership and permission checks. Validate type and size before storage, and do not trust a browser-supplied MIME field. Read [PHP security fundamentals](/programming/php/security/fundamentals/) before building a public upload route. Prepared statements protect the SQL grammar, while upload validation, storage permissions, output headers, and authorization protect separate boundaries. ## Common Pitfalls ### Selecting Image Bytes on List Pages Symptom: a product list consumes excessive memory and transfers far more data than it displays. Cause: a broad `SELECT` list includes every BLOB value. Fix: select metadata columns for lists and fetch one binary value only on the image-serving path. ### Using Original Filenames as Paths Symptom: uploads overwrite each other or create unsafe storage paths. Cause: the application turns user-controlled filenames into object keys. Fix: generate a random server-side key, retain the basename only as display metadata, and keep authorization out of the path string. ### Leaving Orphans After a Failed Write Symptom: storage contains objects without rows, or rows point to missing objects. Cause: the file write and SQL write were treated as one transaction. Fix: define the operation order, record failures, retry cleanup, and run reconciliation using stored keys and checksums. ### Backing Up Only Half the System Symptom: a restored database contains image rows whose objects are unavailable. Cause: database and external storage retention were planned separately. Fix: back up both systems, align retention, and test a restore that opens representative images through restored metadata. ## Frequently Asked Questions ### Should images be stored in a database? Store image bytes in the database when they are small, transactionally tied to the row, and worth carrying through database backups and replication. Store ordinary web images in a controlled file or object store when independent serving, caching, lifecycle rules, or large files matter more. ### What should the database store for an external image? Store a generated object key, owner relationship, MIME type, byte size, dimensions, checksum, processing state, visibility, and timestamps. Keep the original filename only as untrusted display metadata. The row should describe the image without depending on a public URL that can change. ### Why can reading a BLOB use so much memory? MySQL may enlarge an internal buffer to hold the largest BLOB value read during a scan, while a client driver may allocate another buffer as the value crosses the connection. A single large value can therefore raise memory use on both sides even when the query returns only one row. ### How should an application replace an image? Write and validate the replacement first, update the database record or insert a new version, then queue deletion of the old object after the replacement is usable. Record failures for retry because SQL and external storage do not share one atomic transaction. ### How do you serve a private image without exposing a public, guessable URL to it? Route the request through a PHP script that checks the visitor's session and permissions before reading the file, instead of linking directly to a path under the web root. Store the file outside the public directory so a leaked or guessed URL cannot bypass the check. ### Should the file extension of an uploaded image be trusted to determine its type? No. A renamed file can carry any extension regardless of its actual content. Verify the type from the file's real content, such as its detected MIME type or the dimensions an image library reads from it, and reject anything that fails that check before storing metadata or a path. ## What to Read Next Use [SQL schema design basics](/programming/sql/schema-design/) to model ownership and lifecycle state. Continue with [PostgreSQL](/programming/sql/databases/postgresql/) or [MySQL](/programming/sql/databases/mysql/) for database-specific storage and recovery behavior. --- ## SQL Transactions and ACID URL: /programming/sql/transactions/acid/ Cluster: Programming > Sql A [database transaction](/programming/sql/) works like a transfer slip at a bank counter. The slip records money leaving one account and entering another, and the clerk completes the transfer only when both entries can be posted. A half-finished slip cannot become the final ledger. Wrap statements in one transaction whenever they belong to one business action, and keep that transaction short. ACID names the guarantees that make this work: atomicity, consistency, isolation, and durability. ## What ACID Means A transaction groups SQL statements into one unit of work. PostgreSQL's transaction tutorial describes that unit as all or nothing: intermediate states stay hidden from concurrent transactions, and a failure can leave every step without effect. - Atomicity keeps all related steps together. Every step commits, or every step rolls back. - Consistency keeps constraints and other database rules true before and after the transaction. - Isolation controls what concurrent transactions can observe and how their changes interact. - Durability protects a committed result against the failures covered by the database and its configuration. The transfer slip provides one model for all four properties. Atomicity keeps the debit and credit together. Consistency prevents an invalid balance or missing account reference. Isolation keeps another clerk from acting on a half-posted transfer. Durability means a completed transfer remains recorded after the database confirms it. ## Atomicity Makes the Transfer All or Nothing Atomicity prevents a multi-step operation from stopping in a misleading middle state. A transfer must not debit the sender and then lose the credit because a later statement fails. This example assumes `account_entries.account_id` has a foreign key to `accounts.id`. The second insert fails because account `999999` does not exist, so the application rolls back the debit as well. ```sql BEGIN; UPDATE accounts SET balance_cents = balance_cents - 5000 WHERE id = 1; INSERT INTO account_entries (account_id, amount_cents) VALUES (999999, 5000); -- foreign-key error ROLLBACK; ``` After `ROLLBACK`, account 1 has its original balance. The database has not accepted one line from the transfer slip while rejecting the other. Applications must still handle transaction errors correctly. An error does not give every database product and client library identical behavior, so the error path should issue `ROLLBACK` and discard the failed unit of work. ## Consistency Keeps Every Rule True Consistency means the transaction moves the database from one valid state to another. The database can enforce only the rules expressed through constraints, data types, triggers, or correct transaction logic. A check constraint can stop the transfer slip from taking an account below zero: ```sql ALTER TABLE accounts ADD CONSTRAINT balance_cannot_be_negative CHECK (balance_cents >= 0); BEGIN; UPDATE accounts SET balance_cents = balance_cents - 15000 WHERE id = 1; -- fails when the balance is 10000 ROLLBACK; ``` The failed update leaves the prior balance intact. A foreign key can protect account references, while a unique constraint can stop the same transfer identifier from being recorded twice. Application validation still improves error messages, but it cannot replace database constraints. Another controller, import script, background worker, or administration tool can write to the same tables. Put the final rule where every writer must obey it. Some rules span several rows and need both constraints and transaction logic. A transfer can lock the source account, verify the available balance, and write both ledger entries before committing. The transaction keeps those checks and changes together while the constraints reject invalid values at the final database boundary. ## Isolation Controls Concurrent Changes Isolation matters when two requests reach the same rows at nearly the same time. Without the right transaction behavior, both can read the old balance or inventory count and both can decide that their change is safe. A locking read makes the account row part of the transaction's protected work: ```sql BEGIN; SELECT balance_cents FROM accounts WHERE id = 1 FOR UPDATE; UPDATE accounts SET balance_cents = balance_cents - 5000 WHERE id = 1; COMMIT; ``` A competing transaction that tries to lock the same row must wait until this transaction commits or rolls back. The second clerk cannot take the same ledger page and silently post against an earlier balance. Isolation levels change which concurrency anomalies are possible. PostgreSQL documents dirty reads, nonrepeatable reads, phantom reads, and serialization anomalies. Its Read Committed level can return different committed data to two successive queries in one transaction, while Serializable can abort a conflicting transaction so the application can retry it. MySQL InnoDB has its own defaults and locking details. Treat each database manual as the authority, then write a concurrency test for the business rule that matters, such as inventory never becoming negative or a reset token being consumed once. ## Durability Protects a Successful Commit Durability begins after the database reports a successful commit. The completed change should survive the crashes and restart scenarios covered by the engine, storage, and configuration. ```sql BEGIN; INSERT INTO transfers (id, from_account_id, to_account_id, amount_cents) VALUES (9002, 1, 2, 5000); COMMIT; -- After reconnecting: SELECT amount_cents FROM transfers WHERE id = 9002; ``` The final query should still return `5000`. The transfer slip has moved from pending work to the permanent ledger. Reliable recovery still requires backups alongside database durability. MySQL's InnoDB ACID documentation ties durability to redo logging, flush settings, the operating system, and storage hardware. Replication and backups protect different failure cases, so a successful `COMMIT` should never become the entire recovery plan. ## How to Use BEGIN, COMMIT, and ROLLBACK `BEGIN` starts the explicit transaction, `COMMIT` makes its changes permanent, and `ROLLBACK` cancels its uncommitted changes. A successful transfer has this basic shape: ```sql BEGIN; UPDATE accounts SET balance_cents = balance_cents - 5000 WHERE id = 1; UPDATE accounts SET balance_cents = balance_cents + 5000 WHERE id = 2; COMMIT; ``` The application should verify that both updates changed the expected rows before committing. A statement that matches zero rows may be logically wrong even when the database reports no SQL error. Database client libraries expose affected-row counts in different ways, so check the API used by the application. The rule stays stable. Confirm the transfer changed one sender row and one recipient row before the commit makes either change permanent. When validation after the updates finds a problem, finish with `ROLLBACK` instead: ```sql BEGIN; UPDATE inventory SET quantity = quantity - 1 WHERE id = 42; -- The order check fails in the application. ROLLBACK; SELECT quantity FROM inventory WHERE id = 42; ``` The final query returns the quantity from before the transaction. The rollback has canceled the uncommitted update. ## Common Pitfalls & Debugging ### Why did one statement commit before the next one failed? Autocommit is the usual cause of this failure. MySQL enables autocommit for new connections by default, so each successful statement becomes its own transaction unless the application starts an explicit one. PostgreSQL also wraps an individual statement in an implicit transaction when there is no explicit `BEGIN`. Log the transaction boundary and check the connection used by every statement. A transaction belongs to a connection, so starting it on one connection and running the next query on another leaves the statements in separate units of work. ### Why is another request waiting on a lock? Long transactions hold database resources while application code continues doing unrelated work. A remote API call, report calculation, or user prompt inside the transaction can leave other requests waiting on rows they need. Validate ordinary input before `BEGIN`, perform only the database work needed to protect the rule, and commit promptly. If transactions update the same rows in different orders, they can deadlock. Update shared resources in a consistent order and retry the whole transaction when the database reports a retryable deadlock or serialization failure. ### Why did the same query return different rows? The isolation level may allow a nonrepeatable read or phantom read. Under PostgreSQL Read Committed, each statement sees a snapshot taken when that statement begins, so a later query can see changes committed by another transaction. Identify the required invariant before increasing isolation. Repeatable Read offers a more stable snapshot, while Serializable can reject a transaction whose result cannot match any one-at-a-time execution. Stronger levels require deliberate retry handling and can change blocking or throughput. ## Best Practices for Transaction Boundaries - Group statements by one business action, such as placing an order or moving inventory. - Use constraints for rules that must survive every code path. - Check affected-row counts when a missing row is a logical failure. - Keep network calls, file processing, and email delivery outside the transaction. - Handle rollback in every error path, then rethrow or report the original error. - Make deadlock and serialization retries repeat the complete transaction. - Test concurrency with separate database connections instead of one serial test script. A short transaction with a named invariant is easier to review than a large request handler wrapped in `BEGIN` and `COMMIT`. The boundary should show exactly which database facts must change together. ## Frequently Asked Questions ### Does every SQL statement need an explicit transaction? No. Use an explicit `BEGIN` when several statements must commit or roll back as one unit, or when several reads must share one transaction snapshot or locking rule. If one statement committed before the next failed, see the [autocommit debugging section](#why-did-one-statement-commit-before-the-next-one-failed) above. ### Does ROLLBACK undo everything a program has done? No. `ROLLBACK` reverses uncommitted transactional DML on a transaction-safe engine such as InnoDB. Writes to nontransactional tables remain, and DDL statements that trigger implicit commits cannot be rolled back. It also cannot retract external side effects or work committed by another transaction. ### Which isolation level should an application use? Start with the database default, then test the exact concurrency rule the application must protect. PostgreSQL defaults to Read Committed, while MySQL InnoDB defaults to Repeatable Read. Stronger isolation can prevent more anomalies, but applications must also handle retries and possible blocking. ### Can a transaction span MySQL and an external API? A normal SQL transaction controls work inside one database connection. It does not make an external payment, email, or webhook transactional. Record the database state clearly, commit it, and coordinate later side effects with an outbox, queue, or idempotent retry design. ### What is a deadlock and how should an application handle one? Two transactions each hold a lock the other needs, so neither can continue. The database detects this and kills one with an error. The application should catch that specific error and retry the whole transaction, not just the failed statement. ### Can transactions be nested? Not truly, but savepoints give you the useful part. A savepoint marks a point inside a transaction you can roll back to without discarding everything before it, which is how libraries implement what looks like a nested transaction. ### What happens to an open transaction if the connection drops? The database rolls it back. Uncommitted work is discarded when the session ends, which is the behavior you want. A half-finished transfer disappears rather than half-applying. Anything already committed stays committed. ## Self-Check 1. Which ACID property prevents a transfer from committing only the debit: atomicity, consistency, isolation, or durability? 2. What happens when an InnoDB transaction updates two rows, the second update fails, and the application issues `ROLLBACK`? 3. If an inventory row starts at quantity 8, a transaction subtracts 1, rolls back, and then selects the row, what quantity is returned? 4. Which operation belongs outside a short database transaction: updating an account row, inserting a ledger entry, calling a remote payment API, or checking an affected-row count? 5. With MySQL autocommit enabled, what happens when two updates run without `BEGIN` and the second update fails? ### Answers 1. Atomicity. It keeps the debit and credit in one all-or-nothing unit. 2. Both uncommitted row changes are reversed. InnoDB supports transactional DML, so the failed unit returns to its prior state. 3. 8. The rollback cancels the uncommitted subtraction before the later selection runs. 4. Calling a remote payment API. A database rollback cannot retract an external request, and the network wait keeps locks open longer. 5. The first update remains committed. Autocommit makes each statement its own transaction unless the application starts an explicit transaction. ## Next Steps Use [schema design basics](/programming/sql/schema-design/) to model the constraints that consistency depends on, then read [SQL indexes and query optimization](/programming/sql/indexes/query-optimization/) to understand the cost of the reads and writes inside a transaction. For application code, [PHP and MySQL with PDO](/programming/php/mysql/pdo/) shows transaction methods and prepared statements, while [SQL joins](/programming/sql/joins/) covers the relationships those transactions update. --- ## Swift allSatisfy: Check That Every Element Passes a Test URL: /programming/swift/collections/allsatisfy/ Cluster: Programming > Swift --- import Code from "@/docs/components/code/Code.astro"; import { swiftAllSatisfy } from "@/docs/data/astroDocs/clusters/programmingSwift"; import DocsLayout from "@/docs/layouts/DocsLayout.astro"; const headings = [ { depth: 2, slug: "syntax-and-what-it-returns", text: "Syntax and What It Returns" }, { depth: 2, slug: "validating-a-collection-of-values", text: "Validating a Collection of Values" }, { depth: 2, slug: "validating-a-collection-of-structs", text: "Validating a Collection of Structs" }, { depth: 2, slug: "why-does-an-empty-collection-return-true", text: "Why does an empty collection return true?" }, { depth: 2, slug: "allsatisfy-against-filter-and-contains", text: "allSatisfy Against filter and contains" }, { depth: 2, slug: "pitfalls-and-debugging", text: "Pitfalls and Debugging" }, { depth: 3, slug: "force-unwrapping-inside-the-closure", text: "Force-unwrapping Inside the Closure" }, { depth: 3, slug: "side-effects-that-stop-early", text: "Side Effects That Stop Early" }, { depth: 3, slug: "the-dictionary-element-is-a-pair", text: "The Dictionary Element Is a Pair" }, { depth: 2, slug: "frequently-asked-questions", text: "Frequently Asked Questions" }, { depth: 3, slug: "does-allsatisfy-check-every-element", text: "Does allSatisfy check every element?" }, { depth: 3, slug: "what-is-the-opposite-of-allsatisfy", text: "What is the opposite of allSatisfy?" }, { depth: 3, slug: "can-the-closure-throw-an-error", text: "Can the closure throw an error?" }, { depth: 3, slug: "does-allsatisfy-work-on-a-dictionary", text: "Does allSatisfy work on a dictionary?" }, { depth: 3, slug: "is-allsatisfy-faster-than-a-for-loop", text: "Is allSatisfy faster than a for loop?" }, { depth: 3, slug: "why-is-it-allsatisfy-and-not-all", text: "Why is it allSatisfy and not all?" }, { depth: 2, slug: "self-check", text: "Self-Check" }, { depth: 3, slug: "answers", text: "Answers" }, { depth: 2, slug: "related", text: "Related" }, ]; const faqPairs = [ { question: "Does allSatisfy check every element?", answer: "Only until one fails. It stops at the first element that returns false and hands back false immediately. When every element passes it does read the whole collection, because that is the only way to know.", }, { question: "What is the opposite of allSatisfy?", answer: "There is no allFail method. Use contains(where:) for whether any element matches, and negate it for whether none do. Writing !numbers.contains { $0 Before you drive a car, one failed check is enough to stop you. A flat battery does not care that the tyres, oil, and fuel are all fine. The `allSatisfy(_:)` method asks that same question of a Swift collection and gives you a single yes or no: ```swift let pressures = [32, 30, 0, 33] print(pressures.allSatisfy { $0 > 0 }) ``` ```text // Program output: // false ``` ## Syntax and What It Returns The method lives on `Sequence`, so every array, set, dictionary, and range has it. It takes one closure and returns a `Bool`: ```swift func allSatisfy(_ predicate: (Element) throws -> Bool) rethrows -> Bool ``` The `predicate` parameter is called once per element and answers whether that element passes. The result is `true` only when every call answered `true`. The `rethrows` keyword means the call throws only if your closure does. It also short-circuits. The moment a call returns `false`, the method stops and returns `false` without touching the rest of the collection. ## Validating a Collection of Values The most common use is a guard over simple values. Both arrays and sets work, since neither ordering nor duplicates change the answer: ```swift let names = ["John", "Ethan", "Jason"] print(names.allSatisfy { $0.hasSuffix("n") }) let scores: Set = [88, 95, 72, 80] print(scores.allSatisfy { $0 >= 70 }) let stock = [25, 50, 0, 40] print(stock.allSatisfy { $0 > 0 }) ``` ```text // Program output: // true // true // false ``` Each line reads close to the sentence you would say out loud. The third one is the interesting case because it stops at the zero and never looks at the `40` behind it. ## Validating a Collection of Structs The method earns more of its keep on modelled data, where the closure names a property instead of comparing the whole element: ```swift struct SensorReading { let measurement: Double let isAccurate: Bool } let readings = [ SensorReading(measurement: 25.3, isAccurate: true), SensorReading(measurement: 30.1, isAccurate: true), SensorReading(measurement: 29.8, isAccurate: false), ] let usable = readings.allSatisfy { $0.isAccurate && $0.measurement > 0 } print(usable ? "Ready to process." : "Check the sensors.") ``` ```text // Program output: // Check the sensors. ``` Two conditions combine inside one closure, which is fine while they still read as one rule. Once a predicate needs three lines and a comment, move it to a named method on the element type and call that instead. ## Why does an empty collection return true? This is the behaviour that surprises people, and it is deliberate. With no elements present, there is no element that fails, so the honest answer to "do all of them pass?" is yes: ```swift let noTyres: [Int] = [] print(noTyres.allSatisfy { $0 > 0 }) ``` ```text // Program output: // true ``` Logicians call that vacuous truth, and it is the standard definition behind universal-predicate operations in other languages too. It becomes a bug only when your code treats the answer as "this car is safe to drive" rather than "nothing here failed a check". When emptiness is itself invalid, say so in the condition rather than expecting the method to guess: ```swift let tyres: [Int] = [] let roadworthy = !tyres.isEmpty && tyres.allSatisfy { $0 > 0 } print(roadworthy) ``` ```text // Program output: // false ``` ## allSatisfy Against `filter` and `contains` Three methods take a very similar closure and answer three different questions. Picking the wrong one usually shows up as extra work rather than a compile error: | Behaviour | allSatisfy | contains | filter | | Question answered | Do all pass? | Does any pass? | Which ones pass? | | Return type | Bool | Bool | Usually [Element] | | Stops early | On first failure | On first match | No, reads everything | | Empty collection | true | false | Empty result | The contains column compares the predicate-based `contains(where:)` method. The filter method preserves Set and Dictionary collection kinds. Reach for `allSatisfy` when the next line is an `if`. Reach for `filter` when the next line needs the offending elements, usually to show the user which fields failed. Building the filtered collection just to compare its count against the original does the same job with more allocation. ## Pitfalls and Debugging ### Force-unwrapping Inside the Closure A collection of optionals is where this method turns a validation into a crash. The forced unwrap runs before the comparison, so the `nil` traps: ```swift let readings: [Double?] = [25.3, nil, 30.1] let bad = readings.allSatisfy { $0! > 20 } ``` ```text // Typical Swift trap message (source prefix omitted): // Fatal error: Unexpectedly found nil while unwrapping an Optional value ``` Decide what `nil` means and encode that decision. If a missing reading is a failure, compare against the optional directly. If it should be skipped, drop the missing values first: ```swift let strict = readings.allSatisfy { reading in guard let reading else { return false } return reading > 20 } let lenient = readings.compactMap { $0 }.allSatisfy { $0 > 20 } print(strict, lenient) ``` ```text // Program output: // false true ``` ### Side Effects That Stop Early Short-circuiting is a feature until you put a `print` or a log call inside the closure. The output stops at the first failure, which reads like the collection was truncated: ```swift let numbers = [2, 4, 5, 6] _ = numbers.allSatisfy { print("Checking \($0)") return $0 % 2 == 0 } ``` ```text // Program output: // Checking 2 // Checking 4 // Checking 5 ``` The `6` was never examined. Use a `for` loop when the point of the pass is the side effect, and keep this method for the question it answers. ### The Dictionary Element Is a Pair Iterating a dictionary hands the closure a key-value tuple, so a comparison written for a plain value fails to compile: ```swift let fields = ["email": "a@b.com", "username": ""] let filled = fields.allSatisfy { !$0.value.isEmpty } print(filled) ``` ```text // Program output: // false ``` Name the half you mean, either `$0.key` or `$0.value`. Writing `{ !$0.isEmpty }` here produces a type error about the tuple, which is the compiler pointing at the right line for the wrong-sounding reason. ## Frequently Asked Questions ### Does allSatisfy check every element? Only until one fails. It stops at the first element that returns `false` and hands back `false` immediately. When every element passes it does read the whole collection, because that is the only way to know. ### What is the opposite of allSatisfy? There is no `allFail` method. Use `contains(where:)` for whether any element matches, and negate it for whether none do. Writing `!numbers.contains { $0 < 0 }` reads as no element is negative. ### Can the closure throw an error? Yes. The method is marked `rethrows`, so a throwing closure makes the call throwing and you write `try collection.allSatisfy { ... }`. A non-throwing closure leaves the call non-throwing, with no `try` needed. ### Does allSatisfy work on a dictionary? Yes, because `Dictionary` is a `Sequence`. Each element is a key-value pair rather than a single value, so the closure reads `$0.key` or `$0.value`. Comparing `$0` directly against a number will not compile. ### Is allSatisfy faster than a for loop? It is not meant to be faster. A loop with an early `break` does the same asymptotic work and short-circuits the same way, so benchmark if speed actually matters. The gain is that the intent is stated in one line, so a reader does not have to reconstruct it from a flag variable. ### Why is it allSatisfy and not all? Swift names methods after what they do to their argument, and this one asks whether all elements satisfy a predicate. The trailing underscore-colon form, `allSatisfy(_:)`, is how the standard library writes a single unlabelled parameter. ## Self-Check 1. What does `allSatisfy` return for an empty collection, and why? 2. Given `["Alice", "Anna", "Andrew"]`, what does `allSatisfy { $0.hasPrefix("A") }` return? 3. Which method would you use to get back the elements that failed? 4. What happens when the closure force-unwraps an optional that is `nil`? 5. Why can a `print` inside the closure appear to skip elements? ### Answers 1. `true`. No element fails, because there are no elements. Add a `!collection.isEmpty` check when emptiness should be a failure. 2. `true`. All three names start with a capital A, so every call to the predicate answers `true`. 3. `filter`. Invert the predicate to collect the failures, for example `fields.filter { $0.value.isEmpty }`. 4. The program traps. The unwrap runs before the comparison, so it is a runtime crash rather than a `false` result. 5. Short-circuiting. The method stops at the first failure, so nothing after that element is ever passed to the closure. ## Related - [Sets in Swift](/programming/swift/collections/sets/) - the other collection this method is most used on. - [Understanding typealias in Swift](/programming/swift/types/typealias/) - name the closure type once when a predicate is reused. - [Swift](/programming/swift/) - the language hub and its learning path. --- ## Swift Sets: Unique Values, Set Algebra, and Membership Tests URL: /programming/swift/collections/sets/ Cluster: Programming > Swift Think of a set as the guest list on a nightclub door. The bouncer only ever answers one question: is this name on the list? Writing a name down twice changes nothing, and nobody asks who is fourth from the top. That door list is the mental model for Swift's `Set`, and it explains every rule that follows. Uniqueness is enforced for you, order is not stored at all, and membership is the operation the whole type is built around. Arrays answer a different question, which is why the two are not interchangeable. ## What Is a Set in Swift? A `Set` is an unordered collection of unique values of one type. Insert a value that is already there and the set is unchanged. Ask whether a value is present and you get an answer without walking the collection. Three terms carry the rest of the guide. Uniqueness means at most one copy of any value. Unordered means there is no first element and no index. Hashable is the protocol that makes both of those possible, because a set finds values by hashing them rather than by scanning. Arrays and dictionaries are the neighbours readers confuse it with. An array keeps order and allows duplicates. A dictionary maps unique keys to values. A set is closest to a dictionary that kept only the keys. ## How do you create a set? A set is written with an array literal, so the type annotation is what tells Swift which collection you meant: ```swift var fruits: Set = ["Apple", "Banana", "Orange"] var scores: Set = [88, 95, 72] var empty = Set() print(fruits.count, scores.count, empty.isEmpty) ``` ```text // Program output: // 3 3 true ``` The second line leans on inference: `Set` without angle brackets still tells the compiler the collection kind, and `Int` comes from the values. The third line needs the full type because an empty literal says nothing at all. Leave the annotation off entirely and you get an `Array`. That is the single most common surprise with this type, and it comes back in the pitfalls section. ## Adding, Removing, and Checking Membership Four methods cover almost all day-to-day use. Note what `insert` and `remove` hand back, because the return values are where the uniqueness rule becomes visible: ```swift var colors: Set = ["Red", "Green"] let added = colors.insert("Blue") let repeated = colors.insert("Red") let removed = colors.remove("Green") let missing = colors.remove("Purple") print(added.inserted, repeated.inserted) print(removed as Any, missing as Any) print(colors.contains("Blue")) ``` ```text // Program output: // true false // Optional("Green") nil // true ``` The `insert(_:)` method returns a tuple whose `inserted` flag tells you whether anything changed, so a duplicate is reported rather than silently swallowed. The `remove(_:)` method returns the element it took out, or `nil` when the value was never there. Both return values are discardable, so `colors.insert("Blue")` on its own compiles without a warning. Reach for the result when "was this already known?" is the question you are asking. ## Set Algebra: Union, Intersection, and Difference Sets carry the operations you met in school mathematics, and this is where they earn their place over an array. Each one returns a new set and leaves both operands alone. ### The Four Combining Operations ```swift let owned: Set = ["swift", "rust", "go"] let wanted: Set = ["go", "zig", "swift"] print(owned.union(wanted).sorted()) print(owned.intersection(wanted).sorted()) print(owned.subtracting(wanted).sorted()) print(owned.symmetricDifference(wanted).sorted()) ``` ```text // Program output: // ["go", "rust", "swift", "zig"] // ["go", "swift"] // ["rust"] // ["rust", "zig"] ``` Read them as questions about two lists. Union is everything either side knows about. Intersection is the overlap. Subtracting is what the first side has that the second does not, and symmetric difference is everything that appears on exactly one side. The `sorted()` calls are there for the printout only. Without them the order of each result would be unpredictable, which is the whole point of an unordered collection. ### Subset, Superset, and Disjoint Three more methods answer relationship questions and return a `Bool`: ```swift let core: Set = ["read", "write"] let admin: Set = ["read", "write", "delete"] let billing: Set = ["invoice", "refund"] print(core.isSubset(of: admin)) print(admin.isSuperset(of: core)) print(core.isDisjoint(with: billing)) ``` ```text // Program output: // true // true // true ``` The pair that gets mixed up is `isSubset(of:)` and `isDisjoint(with:)`. Subset asks whether every element is contained in the other set; disjoint asks whether no element is. They are opposite ends of the same question. One edge worth knowing: an empty set is a subset of everything, and it is disjoint from everything. Both answers are `true` at once, and neither is a bug. ### Changing a Set in Place Each combining operation has a mutating twin that writes back into the receiver instead of returning a new set. The naming is consistent enough to guess: ```swift var permissions: Set = ["read"] permissions.formUnion(["write", "delete"]) permissions.subtract(["delete"]) print(permissions.sorted()) ``` ```text // Program output: // ["read", "write"] ``` The four pairs are `union` and `formUnion`, `intersection` and `formIntersection`, `subtracting` and `subtract`, `symmetricDifference` and `formSymmetricDifference`. A variable declared with `let` only gets the returning forms, which is the compiler doing its job. ## Storing Your Own Types in a Set Any type can go in a set once it conforms to `Hashable`. For a struct whose stored properties are all `Hashable`, adding the conformance is usually the whole job, because Swift synthesises the implementation: ```swift struct Tag: Hashable { let name: String let colour: String } var tags: Set = [Tag(name: "swift", colour: "orange")] let again = tags.insert(Tag(name: "swift", colour: "orange")) print(tags.count, again.inserted) ``` ```text // Program output: // 1 false ``` The synthesised conformance uses every stored property, so two tags are equal only when both the name and the colour match. Change the colour and you get a second member, which may or may not be what you meant. When identity should rest on fewer properties, write the conformance yourself and keep the two halves in agreement: ```swift struct User: Hashable { let id: Int var displayName: String static func == (lhs: User, rhs: User) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } } ``` The contract is one-directional and strict. Equal values must produce equal hashes. Hashing a property that `==` ignores breaks lookups in ways that are painful to debug, so both members here read the same single field. Synthesis also covers enums, including those with associated values, as long as every associated type is itself `Hashable`. Classes get neither member synthesized; you write both by hand, and a class also has reference identity, which is a separate idea from the value equality a set uses. ## Iterating, Sorting, and Filtering A set is a `Sequence`, so the usual tools work. What changes is that you cannot rely on the order of what comes out: ```swift let sizes: Set = [42, 8, 15, 16, 23, 4] for size in sizes.sorted() { print(size, terminator: " ") } print("") let large = sizes.filter { $0 > 15 } print(type(of: large), large.sorted()) ``` ```text // Program output: // 4 8 15 16 23 42 // Set [16, 23, 42] ``` Two details matter here. Calling `sorted()` on a set gives you an `Array`, because ordering is not something a set can hold. Calling `filter` on a set gives you another `Set`, which is why the printed type is `Set` rather than an array. Mapping behaves differently again. `map` returns an array, since the transformed values may not be `Hashable` and may not be unique. Wrap the result in `Set(...)` when you want the uniqueness back. ## What does constant-time lookup actually mean? The headline claim is that `contains`, `insert`, and `remove` on a set run in constant time on average, while `contains` on an array runs in linear time. Both halves need their scope stated. Constant time here means the average case for a well-behaved `Hashable` type. A hash function that returns the same value for many elements degrades lookups toward linear time, and a custom `hash(into:)` that combines only a low-variety property is the usual way that happens. The array comparison is about searching for a value, not about every operation. Reading `array[3]` is constant time and a set cannot do it at all. Sets win on the specific question of membership. Sizes matter too. Searching an array of eight elements is fast enough that the difference is noise. The gap becomes worth designing around when the collection is large, the lookup happens in a loop, or both. ## A Worked Example: Tracking Tags Here is the type in use. An article has tags, a reader has muted tags, and the app has to decide whether to show the article and what to display: ```swift struct Article { let title: String let tags: Set } let article = Article( title: "Sets in Swift", tags: ["swift", "collections", "beginner"] ) let muted: Set = ["beginner", "opinion"] let isMuted = !article.tags.isDisjoint(with: muted) let visibleTags = article.tags.subtracting(muted).sorted() print(isMuted) print(visibleTags) ``` ```text // Program output: // true // ["collections", "swift"] ``` Every step is one call because the data shape fits the question. The muting check is a disjointness test, the display list is a subtraction, and the sort happens only at the edge where a human sees the result. Write the same logic over arrays and you get nested loops, a manual de-duplication pass, and a decision about what to do with a tag that appears twice. The set answered that last question when the data was created. ## Pitfalls and Debugging ### The Literal That Quietly Became an Array This is the mistake everyone makes once. Nothing fails at the point of the mistake, and the compiler complains somewhere else entirely: ```swift var ids = [1, 2, 2, 3] print(ids.count) var uniqueIds: Set = [1, 2, 2, 3] print(uniqueIds.count) ``` ```text // Program output: // 4 // 3 ``` The first declaration is an array of four, duplicate included. The error usually surfaces later as `value of type '[Int]' has no member 'insert'` or a complaint about `union`. When a set method is missing, check the declaration first. ### A Mutated Element Lost Inside Its Own Set Swift's value semantics prevent the classic version of this bug, where mutating a stored object changes its hash and the element becomes unfindable. Inserting a struct copies its stored properties, so nothing you do to the original afterwards can move it, as long as none of those properties is a reference. The exception is reference-backed state, because the set stores a reference and not the object behind it. Mutating a property that `hash(into:)` reads leaves the element in the wrong bucket, so `contains` reports `false` for something the set still holds. A struct can hit this too when one of its stored references feeds the hash. Hash only state that cannot change after insertion, whichever kind of type you are storing. ### Order You Did Not Ask For A test that asserts on `Array(mySet)` or on a printed set will pass locally and fail elsewhere. Swift seeds hashing per process by default, so the order can differ between runs of the same binary. The fix is to sort before comparing, or to compare sets to sets. Set equality ignores order by definition, so `result == [3, 1, 2]` is both correct and stable. ## Choosing Between Set, Array, and Dictionary Start from the question the collection has to answer rather than from the data you happen to have: | Behaviour | Set | Array | Dictionary | | Keeps insertion order | No | Yes | No | | Allows duplicates | No | Yes | Unique keys only | | Access by index | No | Yes | By key | | Membership search | Constant, average | Linear | Constant, average | | Element requirement | Hashable | Any type | Hashable keys | | Carries a payload | No | No | Yes | Choose a set when duplicates are meaningless and membership is the question: selected identifiers, applied filters, seen items, granted permissions. Choose an array when order carries meaning or a value can legitimately repeat, such as a feed or a list of events. Choose a dictionary when each unique thing has something attached to it. A set of user identifiers tells you who is present; a dictionary keyed by user identifier tells you what each one is doing. Sets also make good intermediate values inside a function even when the result is an array. Building a `Set` to de-duplicate and then calling `sorted()` is clearer than a manual containment loop, and it does less work. ## Frequently Asked Questions ### Are Swift sets thread-safe? No. Value semantics do not make concurrent mutation safe, so give the set a single owner such as an actor or serialise access through one queue. Concurrent reads are safe when nothing mutates the set and its elements are safe to share, which under strict concurrency means the element type is Sendable. ### Can a set hold optional values? Yes. `Optional` is `Hashable` whenever its wrapped type is, so `Set` is a legal type and `nil` is one ordinary member of it. It is rarely what you want, because the `nil` case usually deserves its own name rather than a slot in a collection. ### Why does my set print in a different order each run? Swift seeds its hashing per process by default, so the iteration order of the same set can differ between launches. Sort the values when you need a stable order, and never write a test that depends on the printed order. ### Can you use a set in a SwiftUI State property? Yes. `Set` is a value type, so it stores cleanly in a `@State` property and SwiftUI updates the views that read it when the value changes. Sets of identifiers are a common way to hold multi-selection state in a list. ### When would you reach for NSSet instead? Only when an Objective-C API asks for it, or when you need the reference semantics of `NSMutableSet` on purpose. In code that stays in Swift there is no reason to prefer it, and you lose the generic element type. ### Can a set hold values of different types? Not directly, because `Set` is generic over one element type. `Set` will accept mixed values, at the cost of losing the compiler's help. An enum with a case per shape is usually the better model. ### Does a set have a capacity you can reserve? Yes. `Set(minimumCapacity:)` allocates storage up front, and `reserveCapacity(_:)` grows an existing set. Both avoid repeated rehashing when you already know roughly how many elements are coming. ## Self-Check 1. What type does `var ids = [1, 2, 3]` create, and what does it take to make it a set? 2. What does `insert(_:)` return, and what does the returned flag tell you? 3. Which method asks whether two sets share nothing at all? 4. What does `filter` on a `Set` return, and how does `sorted()` differ? 5. What must be true of two values whose `==` says they are equal? 6. Why can an element become unfindable inside the set that is still holding it? 7. Given `let a: Set = [1, 2]` and `let b: Set = [2, 3]`, what is `a.symmetricDifference(b)`? ### Answers 1. An `Array`. Add a type annotation: `var ids: Set = [1, 2, 3]`, or the fuller `Set`. 2. A tuple of `(inserted: Bool, memberAfterInsert: Element)`. The `inserted` flag is `false` when an equal value was already present, so nothing changed. 3. `isDisjoint(with:)`. It returns `true` when no element appears in both sets, which is the opposite question to `isSubset(of:)`. 4. `filter` returns a `Set`; `sorted()` returns an `Array`. Ordering cannot be stored in a set, so sorting has to hand back a different type. 5. Their hash values must match. Hashing a property that `==` ignores breaks lookup, so both members must read the same fields. 6. Its hashed state changed after insertion. That happens whenever the element reaches mutable state through a stored reference, which leaves it in the wrong bucket. A struct of plain values cannot do it, because insertion copies it. 7. A set containing `1` and `3`, in no particular order. Symmetric difference keeps the values that appear on exactly one side, so the shared `2` drops out. ## Where to Go Next The door list holds up all the way down. A set answers membership, refuses duplicates, and never promises order, and every method in the standard library follows from those three commitments. Pick the collection from the question, add `Hashable` when your own types need to go in one, and sort only at the point where a person is going to read the result. ## Related - [allSatisfy in Swift](/programming/swift/collections/allsatisfy/) - validate every element of a set or array in one call. - [Structs vs Classes in Swift](/programming/swift/types/structs-vs-classes/) - why value semantics decide how a stored element behaves. - [Swift](/programming/swift/) - the language hub and its learning path. --- ## Structs vs Classes in Swift URL: /programming/swift/types/structs-vs-classes/ Cluster: Programming > Swift Picture two ways to carry groceries home: a sturdy basket, or a reusable bag you and a friend both hold. Hand over the basket and your friend walks away with their own apples. Hand over the bag and you are both reaching into the same one. Swift asks you to make that choice every time you define a type. A struct behaves like the basket and a class behaves like the bag, and the difference reaches further than it first appears: into inheritance, memory, mutability, and identity. Getting it right early saves you from chasing shared-state bugs later. ## What are structs and classes? A struct is a value type. It groups stored properties and methods under one name, and every assignment or function call hands over an independent copy of its stored values. It gets a memberwise initialiser for free unless you write your own initialiser in the same declaration, and it cannot inherit from another struct. A class is a reference type. It groups the same things under one name, but every assignment or function call hands over a reference to one shared instance. It can inherit from another class, it never gets a synthesised memberwise initialiser, and it can define a `deinit`. Both can conform to protocols, hold stored and computed properties, define methods, and be generic. The shared surface is wide enough that the type keyword often looks like a formality, which is why the differences catch people out. ## Quick Comparison The whole comparison at a glance, before the sections that show each row in code: | Behaviour | Struct | Class | | Assignment hands over | An independent copy | A shared reference | | Inheritance | Not available | Single superclass | | Memory management | Scope-based; ARC only for stored references | ARC reference counting | | Changing a property | Needs a var instance | Works on a let instance | | Mutating methods | Marked mutating | No keyword needed | | Identity comparison | Not applicable | Triple equals operator | | Memberwise initialiser | Synthesised unless you write your own | Never synthesised | | Deinitialiser | Not on an ordinary struct | Available | Every row above describes an ordinary struct, which is what the `struct` keyword gives you unless you opt out of copying. That opt-out is a specialist tool and it changes two of these rows, including the deinitialiser. ## How does assignment behave? This is the row every other row descends from. The same two lines produce opposite results depending on which keyword you used: ```swift struct Basket { var apples: Int } class Bag { var apples: Int init(apples: Int) { self.apples = apples } } var myBasket = Basket(apples: 10) var yourBasket = myBasket yourBasket.apples = 5 let myBag = Bag(apples: 10) let yourBag = myBag yourBag.apples = 5 print(myBasket.apples, yourBasket.apples) print(myBag.apples, yourBag.apples) ``` ```text // Program output: // 10 5 // 5 5 ``` The basket split into two independent copies at the assignment. The bag did not. Both names describe one object, so a change made through either name is visible through the other. Passing a value into a function follows the same rule. A struct arrives as a copy, so changes stay inside unless the parameter is marked `inout` or the struct stores a reference. A class instance arrives as a reference the function can mutate on your behalf. ## Which one supports inheritance? Only classes. A class can name one superclass, inherit its properties and methods, and override them: ```swift class Vehicle { var speed = 0.0 func accelerate() { speed += 10 } } class Car: Vehicle { override func accelerate() { speed += 20 } } let car = Car() car.accelerate() print(car.speed) ``` ```text // Program output: // 20.0 ``` Structs have no equivalent, and that is a smaller loss than it sounds. Protocols plus protocol extensions supply shared behaviour to any number of unrelated types, without the single-parent restriction or the fragile base class problem: ```swift import Foundation protocol Priced { var price: Double { get } } extension Priced { var formattedPrice: String { String(format: "$%.2f", price) } } struct Book: Priced { let price: Double } print(Book(price: 12.5).formattedPrice) ``` ```text // Program output: // $12.50 ``` Reach for a class when you need a hierarchy, or when an Apple framework hands you a base class to subclass. Reach for a protocol when what you wanted was shared behaviour. ## How is memory managed? A class instance lives on the heap and is tracked by Automatic Reference Counting. ARC counts how many references point at the instance and frees it when that count reaches zero: ```swift class Session { let name: String init(name: String) { self.name = name print("\(name) opened") } deinit { print("\(name) closed") } } var first: Session? = Session(name: "editor") var second = first first = nil second = nil ``` ```text // Program output: // editor opened // editor closed ``` Note where the closing line appears. Setting `first` to `nil` did nothing visible, because `second` was still holding the instance. Only the second assignment dropped the count to zero. Structs need none of this bookkeeping, because each copy is owned outright by whatever holds it and goes away with its scope. The value itself is not reference-counted, so it cannot form a retain cycle alone, but any reference it stores can still join one that `weak` or `unowned` has to break. ## What does it take to change a stored value? Structs are not immutable. Mutability follows the variable that holds the struct, and a method that changes stored properties has to declare it. ```swift struct Rectangle { var width: Double var height: Double mutating func scale(by factor: Double) { width *= factor height *= factor } } var box = Rectangle(width: 3, height: 4) box.scale(by: 2) print(box.width, box.height) let fixed = Rectangle(width: 3, height: 4) // fixed.width = 10 // error: fixed is a let constant ``` ```text // Program output: // 6.0 8.0 ``` Classes behave differently because `let` constrains the reference rather than the object. A class instance held in a constant still accepts writes to any `var` property, and no `mutating` keyword exists on class methods. That is the practical asymmetry. With a struct, `let` means the value cannot change. With a class, `let` means the name cannot be repointed. ## Equality Against Identity Reference types introduce a question value types do not have: are these the same object, or merely two objects that look alike? Swift gives those two questions different operators. ```swift class Ticket: Equatable { let code: String init(code: String) { self.code = code } static func == (lhs: Ticket, rhs: Ticket) -> Bool { lhs.code == rhs.code } } let a = Ticket(code: "A1") let b = Ticket(code: "A1") let c = a print(a == b, a === b) print(a == c, a === c) ``` ```text // Program output: // true false // true true ``` The `==` operator compares contents using whatever you wrote, and `===` compares instance identity. For a struct only the first question exists, and Swift can synthesise `Equatable` for you when every stored property is already `Equatable`. ## What about performance? The usual claim is that structs are faster. That holds for the common case and needs its scope stated, because the mechanism is allocation. A small struct can live in registers or on the stack, with no allocation of its own and no reference counting unless it stores references. A class instance usually costs a heap allocation, and passing references around usually costs retain and release traffic that the optimiser can sometimes remove. The direction reverses when the struct gets large. Copying a value type with many stored properties through a deep call chain can move many bytes, where a class would have moved one pointer. The compiler may pass a large value indirectly instead, so this is a tendency rather than a guarantee. Swift softens this for the standard library's collections with copy-on-write, so an `Array` or `String` property does not copy its storage until something writes to it. None of this is a reason to pick a type. Pick on semantics, then measure with Instruments if a hot path turns out to matter. ## The Same Model Written Both Ways A concrete job makes the choice obvious. Here is one screen's data written the way each type is meant to be used: ```swift import Foundation struct Money: Equatable { let amount: Decimal let currency: String } final class AccountStore { private(set) var balance: Money init(balance: Money) { self.balance = balance } func apply(_ delta: Decimal) { balance = Money(amount: balance.amount + delta, currency: balance.currency) } } let store = AccountStore(balance: Money(amount: 100, currency: "AUD")) let watcher = store store.apply(-25) print(watcher.balance == Money(amount: 75, currency: "AUD")) ``` ```text // Program output: // true ``` `Money` is a struct because two amounts of seventy-five dollars are the same thing; there is no reason to ask which one is the original. `AccountStore` is a class because the app has exactly one balance, and every screen that reads it must see the same value. That is the test in one sentence. Data gets a struct; the one thing everyone shares gets a class. ## What Choosing Wrong Costs Picking a class for plain data produces bugs that look like haunting. A model edited on a detail screen changes the list behind it, because both were holding the same object, and the fix is a defensive copy at every boundary you remember to write one. Picking a struct for shared state produces the opposite complaint: an update that appears to do nothing. The mutation landed on a copy nobody else is looking at, which shows up in SwiftUI as a value that changes in the debugger and never on screen. Reversing the decision later is not free. Switching a class to a struct means finding every place that relied on shared mutation, and switching a struct to a class means auditing every copy that silently became an alias. Both are mechanical, tedious, and easy to get half right. ## How to Choose Between Them Choose a struct when: - The type describes data rather than a participant, such as a point, a price, or a parsed response. - Two instances with the same contents should count as the same thing. - Copies being independent is what you want, not something to defend against. - Shared behaviour can come from protocols instead of a base class. Choose a class when: - The type has identity: one network client, one cache, one store, and everyone must see the same one. - Mutations made in one place must be visible everywhere the instance is held. - You need inheritance, or an Apple framework hands you a base class to subclass. - You need a `deinit` to release something the type owns, which an ordinary struct cannot give you. When both lists look plausible, Apple's own recommendation is to start with a struct and move to a class only when a listed class reason appears. Starting from the shared-mutable side is harder to walk back. ## Frequently Asked Questions ### Are structs always faster than classes? No. A small struct avoids allocation and reference counting, which usually wins. A struct with many stored properties copied through deep call chains can cost more than passing one reference, so measure before treating it as a rule. ### Can a struct conform to a protocol? Yes, and this is how structs get shared behaviour without inheritance. A protocol extension supplies a default implementation to every conforming type, which covers most of what a base class would have been used for. ### Why can I change a property on a let class instance? The `let` applies to the reference, not to what it points at. The constant guarantees the name keeps pointing at the same instance; any `var` property inside that instance is still free to change. ### Can a struct hold a class as a property? Yes, and it is worth knowing what that does. Copying the struct copies the reference, so both copies share the same object. The struct then has value semantics on paper and shared mutable state in practice. ### What about actors and enums? An enum is a value type and copies like a struct. An actor is a reference type like a class, with the extra rule that code outside its isolation reaches its mutable state through `await`. Code already running on the actor reads it synchronously, and that isolation is how it protects itself from data races. ### Does SwiftUI prefer one over the other? SwiftUI views are structs, and the framework recreates them freely. Long-lived state that several views share is usually a class marked `@Observable`, because SwiftUI needs a stable instance to watch rather than a fresh copy. ## Self-Check 1. After `var b = a`, when does changing `b` also change `a`? 2. Why does a struct method that writes to a stored property need the `mutating` keyword? 3. What does `===` compare that `==` does not? 4. Which of the two can form a retain cycle, and what breaks it? 5. A struct holds a class instance as a property. What happens when the struct is copied? 6. Name one reason to pick a class that has nothing to do with performance. ### Answers 1. When the type is a class. A class assignment copies the reference, so both names describe one instance. A struct assignment copies the value. 2. Because the method may be called on a copy the caller owns. The keyword marks the method as requiring a `var` instance, so calling it on a `let` is a compile error rather than a silent no-op. 3. Instance identity. Two distinct objects can be `==` while `===` is `false`; only `===` answers whether they are the same allocation. 4. Classes. Two instances holding strong references to each other never reach a zero count. Marking one side `weak` or `unowned` breaks the cycle. 5. The reference is copied, not the object. Both struct copies point at the same instance, so mutating through one is visible through the other. 6. Identity. When the app must have exactly one of something, or when a framework requires a subclass, or when a `deinit` is needed to release an owned resource. ## Where to Go Next The basket and the bag come down to one question: does handing something over create a second copy, or share the first? Every other difference in this guide follows from that answer. Ask what the type is before asking what it costs. If it is a fact about the world, copy it; if it is a thing the app owns exactly one of, share it. ## Related - [Understanding typealias in Swift](/programming/swift/types/typealias/) - name a type once, whichever kind you chose. - [Sets in Swift](/programming/swift/collections/sets/) - what value semantics mean for elements stored in a collection. - [Swift](/programming/swift/) - the language hub and its learning path. --- ## Understanding typealias in Swift URL: /programming/swift/types/typealias/ Cluster: Programming > Swift A `typealias` gives an existing type a second name. Nothing new is created and nothing is wrapped. The alias and the original are the same type, and the compiler treats them as interchangeable everywhere. ```swift typealias Age = Int func celebrate(person: String, age: Age) { print("\(person) is now \(age)") } celebrate(person: "Alice", age: 30) ``` ```text // Program output: // Alice is now 30 ``` That signature reads better than one with a bare `Int` in it, and the payoff grows with the type. This keyword does most of its work on closures, tuples, dictionaries, and generic types, where the spelled-out version fills a whole line. ## Syntax ```swift typealias NewName = ExistingType ``` The new name is an identifier that follows the same rules as any type name, so start it with a capital letter. The existing type can be anything the compiler can name: a standard library type, one of yours, a tuple, a closure signature, or a generic specialisation. An alias can be declared at file scope or nested inside a struct, class, enum, or protocol. It also takes access-control keywords, so `private typealias` keeps a helper name out of the rest of the module. ## Naming a Closure You Pass Around Closure types are where the readability gain is largest, because the spelled-out form has to be repeated at every use site: ```swift import Foundation typealias CompletionHandler = (Result) -> Void func fetch(from path: String, then completion: @escaping CompletionHandler) { completion(.success(Data())) } fetch(from: "/articles") { result in switch result { case .success(let data): print("Loaded \(data.count) bytes") case .failure(let error): print("Failed: \(error)") } } ``` ```text // Program output: // Loaded 0 bytes ``` One name now covers the callback for every request in the file. Change the payload later and there is a single line to edit rather than one per function. Note that `@escaping` stays on the parameter, not in the alias. Attributes like that belong to the position the closure is used in, so an alias cannot carry them. ## Naming Tuples and Dictionaries Tuples have no name of their own, which makes them the type most improved by getting one. Naming the elements at the same time is worth the extra characters: ```swift typealias Point2D = (x: Double, y: Double) typealias WordCounts = [String: Int] let origin: Point2D = (x: 0, y: 0) var counts: WordCounts = ["swift": 2] counts["set", default: 0] += 1 print(origin.x, counts.sorted { $0.key < $1.key }) ``` ```text // Program output: // 0.0 [(key: "set", value: 1), (key: "swift", value: 2)] ``` The labelled form gives you `origin.x` instead of `origin.0`, which survives a later reordering of the elements. Dictionary aliases pay off in the same way once the key or value type is long enough to wrap. Once a tuple grows past two or three elements, or starts being returned from public functions, a struct is usually the better call. It gets a real name, methods, and the ability to conform to protocols. ## Generic Aliases An alias can take its own type parameters and pass them through to the underlying type: ```swift typealias StringKeyed = [String: Value] typealias Callback = (T) -> Void let ages: StringKeyed = ["Alice": 30, "Bob": 25] let log: Callback = { print("log: \($0)") } log("\(ages.count) people") ``` ```text // Program output: // log: 2 people ``` You can also pin a generic type down to one concrete case, which is the more common use. `typealias IntStack = Stack` gives a specific specialisation a short name without introducing a parameter. ## `typealias` Inside a Protocol Protocols use `associatedtype` to leave a type open, and conforming types fill it in. A `typealias` is how a conformer states that choice explicitly: ```swift protocol Renderer { associatedtype Output func render(_ text: String) -> Output } struct HTMLRenderer: Renderer { typealias Output = String func render(_ text: String) -> String { "

    \(text)

    " } } print(HTMLRenderer().render("Hi")) ``` ```text // Program output: //

    Hi

    ``` The alias here is usually optional, because Swift infers `Output` from the return type of `render`. Write it out when the inference is not obvious to a reader, or when the compiler cannot work it out on its own. ## What a `typealias` Deliberately Does Not Do It does not give you a distinct type. This compiles, and for some purposes that is a problem: ```swift typealias Metres = Double typealias Seconds = Double let distance: Metres = 100 let time: Seconds = 9.58 let nonsense = distance + time print(nonsense) ``` ```text // Program output: // 109.58 ``` Adding metres to seconds is meaningless, and the compiler had no way to object because both names mean `Double`. When you want the compiler to enforce the distinction, wrap the value in a struct instead: ```swift struct Metres { let value: Double } // Metres(value: 100) + 9.58 no longer compiles ``` That is the trade. An alias costs nothing and buys readability; a wrapper type costs a little ceremony and buys type safety. Choose by whether mixing the values up would be a bug. ## Pitfalls and Debugging ### Circular Reference Two aliases that define each other have no underlying type to resolve to: ```swift typealias A = B typealias B = A ``` ```text // Compiler error: // error: type alias 'A' references itself ``` The fix is to anchor one end on a real type. Aliases can chain, so `typealias A = Int` followed by `typealias B = A` is fine and resolves in one step. ### Cannot Find Type in Scope An alias declared inside a type or marked `private` is not visible from outside that scope, and the error names the alias rather than the scope: ```swift struct Parser { private typealias Row = [String: String] } // let row: Parser.Row = [:] // error: 'Row' is inaccessible due to 'private' protection level ``` Order does not matter at file scope, so an alias used above its declaration still compiles. Access level and nesting are what break lookup, so check those before moving lines around. ### An Alias That Hides What Broke Type errors are reported against the underlying type, which is why messages mention a type you did not write: ```swift typealias Scores = [Int] let scores: Scores = ["a"] ``` ```text // Compiler error: // error: cannot convert value of type 'String' to expected element type 'Int' ``` Nothing is wrong with the alias. Read past the name to what it resolves to, which Xcode shows on hover and the compiler often prints as `Scores` (aka `Array`). ## When is a typealias worth it? Use one when the underlying type is long, repeated, and stable: callback signatures, dictionary shapes that appear in several signatures, and generic specialisations you name often. Those are the cases where the alias removes noise without removing information. Skip it when the alias only renames something short, when the new name is vaguer than the original, or when you wanted a distinct type. An alias that a reader has to look up has cost more than it saved. ## Frequently Asked Questions ### Does a typealias create a new type? No. It introduces a second name for a type that already exists, and the compiler treats the two as interchangeable everywhere. If you want a name the compiler will enforce as distinct, define a struct that wraps the underlying value. ### Can a typealias be private? Yes. It takes the same access levels as any other declaration, so `private`, `fileprivate`, `internal`, and `public` all apply. A public alias to an internal type will not compile, because the name would expose something callers cannot see. ### Can you alias your own class or struct? Yes, and there is no restriction on which kind of type you alias. Writing `typealias Human = Person` is legal, and `Human` then means exactly the `Person` class, including its initialisers and inheritance. ### What is the difference between typealias and associatedtype? A `typealias` names a type that is already known. An `associatedtype` is a placeholder inside a protocol that each conforming type fills in, often by declaring a `typealias`, though the compiler usually infers it from the method signatures. ### Does a typealias cost anything at runtime? No. It is resolved during compilation, so the generated code is identical to writing the underlying type by hand. There is no wrapper, no indirection, and nothing to unwrap. ### Should you alias a simple type like Int? Only when the alias adds meaning a reader needs, such as `Age` or `Milliseconds` in a signature full of numbers. Aliasing `Int` to `Number` adds a name to learn without adding information, which is the usual way this feature gets misused. ## Self-Check 1. What does this print? ```swift typealias Age = Int let myAge: Age = 25 print(myAge) ``` A. A compilation error 2. B. Age 3. C. 25 4. D. 25 years old 5. Which is a valid declaration? A. `typealias Person = struct { name: String }` 6. B. `typealias FullName = (String, String)` 7. C. `alias Name = String` 8. D. `typealias = Int` 9. What is the type of `colour` after `typealias RGB = (Int, Int, Int)` and `var colour: RGB = (255, 0, 0)`? A. A custom RGB struct 10. B. A tuple of three integers 11. C. An array of three integers 12. D. A compilation error 13. How would you name a closure taking two `Int` values and returning a `Bool`? A. `typealias Compare = (Int, Int) -> Bool` 14. B. `typealias Compare = (Int, Int)` 15. C. `typealias Compare = Int -> Bool` 16. D. `typealias Compare = () -> Bool` 17. Given `typealias StringMap = [String: String]`, what is the type of `let result: StringMap = ["name": "Alice"]`? A. `[Int: Int]` 18. B. `[String: Any]` 19. C. `[String: String]` 20. D. `[String]` 21. True or false: a `typealias` creates a type the compiler will keep separate from the original. 22. What is a `typealias` for? A. Creating new data types 23. B. Naming existing types for readability 24. C. Defining constants 25. D. Replacing class inheritance 26. What does this print? ```swift typealias Point2D = (x: Double, y: Double) let point: Point2D = (x: 3.0, y: 4.0) print(point.x) ``` A. 3.0 27. B. 4.0 28. C. A compilation error 29. D. (3.0, 4.0) ### Answers 1. C. `Age` is `Int`, so the value prints as the integer `25`. 2. B. The right side must be an existing type, and a tuple type qualifies. Option A tries to declare a struct, C uses a keyword that does not exist, and D has no name. 3. B. The alias renames the tuple type; it does not build a struct or an array. 4. A. A closure type needs both the parameter list and the return type. Option C is missing the parentheses that Swift requires around parameters. 5. C. The alias resolves to `[String: String]` exactly, with no widening to `Any`. 6. False. The alias and the original are the same type, so mixing them up is not something the compiler can catch. A wrapper struct is what gives you separation. 7. B. It adds a name, not a type, and it has nothing to do with constants or inheritance. 8. A. The labelled tuple gives `point.x`, which holds `3.0`. ## Related - [Structs vs Classes in Swift](/programming/swift/types/structs-vs-classes/) - when a wrapper type beats an alias. - [allSatisfy in Swift](/programming/swift/collections/allsatisfy/) - a natural home for a named predicate alias. - [Swift](/programming/swift/) - the language hub and its learning path. --- ## APIs URL: /programming/topics/apis/ Cluster: Programming > Topics An [application programming interface](/programming/) (API) defines how one system asks another system for data or an action. The URL, HTTP method, credentials, request body, response body, status codes, and retry rules form a contract that both sides can follow. Use this hub to move from that contract to a working PHP endpoint or provider integration. The linked guides cover the code, data, and security boundaries behind the request. ## Start Here - [Build Your Own API with PHP](/programming/php/apis/custom-api/) explains routing, JSON requests, validation, authentication, error responses, and persistent data for a small HTTP API. - [Sending SMS with HTTP APIs](/programming/php/apis/sending-sms/) follows an outbound provider request through consent, delivery callbacks, retries, and safe logging. ## API Contract at a Glance A small contract should make the resource and outcome visible before client code is written. This request queues a message, while the response confirms acceptance without claiming that delivery has finished. ```http POST /api/messages Authorization: Bearer Content-Type: application/json {"to":"+61400111222","body":"Your order is ready"} HTTP/1.1 202 Accepted {"messageId":"msg_8414","status":"queued"} ``` Choose methods and status codes deliberately, keep credentials on the correct side of the boundary, and define whether a failed write can be retried safely. The focused tutorials show how those rules change the implementation. ## Related Programming Guides - [Backend development](/programming/topics/backend/) places APIs beside server-side languages, databases, and deployment concerns. - [PHP security fundamentals](/programming/php/security/fundamentals/) covers validation, authentication, authorization, sessions, and secret handling. - [SQL programming](/programming/sql/) covers the durable records behind API resources, idempotency keys, and audit history. - [Application security](/appsec/) provides the wider security path for public endpoints and integrations. ## Frequently Asked Questions ### What is the difference between an API and a webhook? An API is something your code calls to request data or trigger an action. A webhook reverses that. The other system calls your endpoint when an event happens, so your server must expose a URL that accepts incoming requests. ### What does idempotent mean for an API request? That making the same call several times leaves the same result as making it once. It is what allows a client to retry safely after a timeout without creating a duplicate order, charge, or message. ### What is the difference between REST and GraphQL? REST exposes several fixed endpoints, each returning a predetermined shape. GraphQL exposes one endpoint where the client states exactly which fields it wants, trading cacheable fixed responses for flexible client-defined queries. ### What is API rate limiting? A cap on how many requests a client may make within a time window, protecting the service from overload and enforcing fair use. Requests past the limit are usually rejected with a 429 status until the window resets. ### How is an API key different from OAuth? An API key is one static secret sent with every request: simple, but hard to scope or revoke selectively. OAuth issues short-lived scoped tokens through an authorization flow, so permissions can be granted narrowly and expire on their own. ### What is the difference between a 401 and a 403 response? 401 means the request carried no valid credentials, so the client should authenticate. 403 means the credentials were understood but do not permit that action, so authenticating again will not help. ### What is gRPC? A binary remote-procedure-call framework over HTTP/2 that generates typed client and server code from a shared schema. It trades REST's readable JSON and universal browser support for smaller payloads and faster service-to-service calls. --- ## Backend Development URL: /programming/topics/backend/ Cluster: Programming > Topics [Backend development](/programming/) covers the code that runs on a server: request handling, business logic, talking to a database, and exposing APIs that other software can call. If you are new to the field, start with a language you can already run, then follow the links into databases and API design as the application you are building actually needs them. ## What Backend Work Actually Involves A backend request follows roughly the same shape no matter which language handles it. A client sends a request, the server validates what came in, it reads or writes to a database, and it sends back a response the client can parse, usually JSON. A checkout endpoint checks that a cart is not empty, confirms the items are still in stock, charges a card, and writes an order row before the client sees a response. Because charging and storing the order are separate side effects, a production checkout also needs an idempotency key to make retries safe, transaction boundaries where they apply, and reconciliation when one step succeeds but another fails. The specifics change with the product, but the request, validate, store, respond shape holds across nearly every backend job you will build. That shape breaks down into a handful of concrete pieces you will keep running into: - Authentication and authorization. Confirming who is asking, and whether that account is allowed to do it. - Database queries. Reading and writing rows through SQL, or through an ORM that generates SQL for you. - Business logic. The product's actual rules: pricing, permissions, what happens when an order ships. - Background jobs. Work that should not block the response, like sending an email or resizing an image. - Third-party integrations. Calling a payment processor or another company's API, and handling what happens when it fails. You will not touch all five on day one. A first project usually starts with database queries and a little business logic, then grows into the rest once real users show up. ## Backend Languages The deployment target and the team you already have should guide the language choice. A team already running a Windows or Azure stack may get more done in C# because its hosting and tooling are already understood. A solo project with no existing infrastructure has more freedom, and PHP, Python, or Go can all support a small working API. Reach for Rust when memory control or a strict latency budget is an actual constraint, because it asks you to make more ownership decisions up front in exchange for those guarantees. - [PHP](/programming/php/) is the most direct path from a blank file to a working database-backed site, and it anchors the original CodeWalkers material. - [SQL](/programming/sql/) is the query language every backend relies on once data has to survive a restart. - [Python](/programming/python/) suits scripting, data work, and service code. - [Go](/programming/go/) targets fast, concurrent services and command-line tools. - [Rust](/programming/rust/) trades a steeper learning curve for memory safety without a garbage collector. - [C#](/programming/csharp/) is the typed, Microsoft-stack language; its .NET and ASP.NET Core frameworks are listed below. ## Learning Order Learn the pieces in roughly the same order regardless of which language you pick: 1. Start with the HTTP request and response cycle: methods, status codes, headers, and how a client and server exchange data. 2. Learn enough of the language to validate input, call a function, and return a response. 3. Add a database: model rows, write a query, and run it through the language's driver. 4. Introduce a framework such as ASP.NET Core or Laravel once you can see which conventions it is adding around the HTTP and database layers. Once the cycle makes sense, follow whichever guide suits how you learn. Our [PHP](/programming/php/) guide goes from a blank file to a database-backed page, the fastest way to see the whole cycle end to end. [SQL](/programming/sql/) walks through modeling rows and writing the joins a backend actually runs. [Python](/programming/python/) is a comfortable entry point if you already write scripts and want the language itself to feel familiar. ## How Frontend and Backend Meet A frontend and a backend agree on a contract before they agree on anything else: what URL to call, what data to send, and what shape the response takes back. That contract is the API. [Working with HTTP APIs](/programming/topics/apis/) covers the full request and response shape, including REST, status codes, authentication, and retries; the short version below is enough to see where the boundary sits. A browser sends a request to a URL such as `/api/orders`, usually with a JSON body and authentication credentials in a secure cookie or an `Authorization` header. The backend validates those credentials, checks whether the account may place an order, runs the business logic, and sends back JSON the frontend can parse. Neither side needs to know how the other is built as long as both agree on the request, response, and status codes. That boundary is also where beginner confusion shows up most often. A blank screen usually traces back to a backend error the frontend never handled, easy to miss if you are only reading your own component code. Learning to read a 401 versus a 500 in the network tab saves more debugging time than almost anything else here. ## Picking Your First Backend Language PHP is a practical first choice when you want a database-backed web page and inexpensive hosting. Python may be a better start if you already write scripts or work with data, while Go suits a learner who specifically wants a compiled, statically typed language. The best first language is the one that matches the environment you can run and maintain. C# can be the productive first choice when an existing Windows, Azure, or .NET environment decides the stack. Rust usually makes more sense when a real systems or performance constraint justifies its steeper learning curve. | Language | Best if | First project | | PHP | You want a database-backed page fast | A reading list that creates and updates MySQL rows | | Python | You already script or work with data | A small REST API with a couple of endpoints | | Go | You want compiled speed and real concurrency | A command-line tool or a single API service | Whichever you pick, finish one small project that touches a real database before you go comparing frameworks. A framework choice made before you understand what it is wrapping just adds a layer you cannot see through yet. ## Frameworks The framework guides currently available are: - [.NET and ASP.NET Core](/programming/csharp/frameworks/) for C# web APIs, services, and EF Core data access. - [Laravel and Symfony](/programming/php/frameworks/) for PHP web applications. Start with the language and request cycle, then choose a framework when its conventions solve a problem you can already name. ## Frequently Asked Questions ### Do backend developers need to know CSS? Not deeply, but enough to read a template and understand what the frontend needs. Backend work is judged on data correctness, security, and reliability. Knowing where your output lands still helps you design an API someone can actually use. ### What is the difference between backend development and DevOps? Backend work builds the application: endpoints, data models, business rules. DevOps builds and runs the platform it deploys onto: pipelines, infrastructure, monitoring. The roles overlap at deployment, and on small teams one person often does both. ### How much SQL does a backend developer need? More than most expect. Joins, indexes, transactions, and reading a query plan are daily tools, because an ORM still issues SQL and you will be the one explaining a slow query. Query tuning matters more than memorizing vendor-specific syntax. ### Do you need a computer science degree for backend work? No. A degree helps with algorithms, systems, and getting past some hiring filters, but it is not a requirement. Demonstrable projects, working knowledge of databases and HTTP, and the ability to debug production issues carry more weight in practice. ### Is backend development harder than frontend? Neither is harder; the difficulty sits in different places. Backend problems are usually about data correctness, concurrency, and failure. Frontend problems are about state, browsers, and human behavior. Most people find one more natural than the other. --- ## Coding Challenges: Beginner Exercises and Practice Tips URL: /programming/topics/challenges/ Cluster: Programming > Topics Coding challenges are small programming exercises with a defined task, inputs, and expected results. They give you a way to practice loops, functions, and data handling without building an entire application. Start with a problem you can describe in plain English, then write the code and check it against examples. You can use a language you already know. If you have not chosen one yet, the [programming guide](/programming/) explains where each language fits. CodeWalkers has a history of PHP coding contests, but you do not need to enter a contest to try the exercises below. ## What Are Coding Challenges Good For? Coding challenges help you practice a specific skill, such as looping through a list or counting repeated values. Several solutions may produce the right result, with different costs in memory, speed, or readability. A short challenge can warm you up the way a musician runs scales before a performance. However, exercises do not replace building an application. Projects also teach you to organize files, handle changing requirements, and maintain code that other people use. ## Three Coding Challenges to Try These exercises need variables, functions, conditions, and loops. The last two also use key-value collections. Use Python, PHP, JavaScript, or another language you are learning; no website, database, or extra package is required. ### 1. Add the Positive Numbers Write a function that adds only the positive integers in a list, so `[4, -2, 7, 0]` returns `11`. An empty list or a list containing only zero and negative numbers should return `0`. Start with a running total and inspect each number. Check a single positive number and an all-negative list before calling the function finished. This practices a loop and a condition; [Python lists and loops](/programming/python/) are one way to get started. ### 2. Count Task Statuses Count how often each status appears in a list, treating statuses as exact, case-sensitive strings. For `["open", "done", "open"]`, return counts of `open = 2` and `done = 1`. An empty list should produce no status entries. Use each status as a key and its count as the value. Check repeated statuses and a status that appears once. As an extra task, decide how to handle `"Open"` and `"open"` as the same status, then update your tests to match that rule. ### 3. Total Paid Orders by Customer Given orders with a customer name, an amount in whole cents, and a paid flag, calculate each customer's paid total. Alice's paid orders of `1200` and `800` cents should total `2000`. Exclude an unpaid order of `500` cents. Assume valid records, nonnegative integer amounts, and customer names that match exactly. Include only customers with at least one paid order; no paid orders should produce an empty result. Check two customers and a zero-value paid order. Whole cents keep decimal rounding out of this exercise. Once the function works, try the same task with rows in a database. The [SQL guide](/programming/sql/) explains the query basics you will need to filter records and group totals. ## How Do You Solve a Coding Challenge? Solve a coding challenge by stating the expected result, writing a simple solution, and checking ordinary and unusual inputs before optimizing it. 1. Restate the rules. Write down the inputs, output, and limits. Decide what an empty input should do instead of guessing midway through the code. 2. Work one example by hand. Trace the values you expect at each step. If you cannot explain the steps, reduce the problem first. 3. Write the simplest working version. Use clear names and straightforward logic. Get the sample result right before trying to make the solution shorter or faster. 4. Look for an input that breaks it. Try empty lists, repeated values, zero, and boundary values when the rules allow them. Save those cases so you can rerun them after a change. 5. Compare approaches. After your attempt, read another solution and explain what it does differently. Optimize when the input limits or measured performance give you a reason. A passing sample is a useful first check, not proof that every input works. For the order exercise, a solution that handles Alice correctly may still mix two customers' totals. Add a second customer to test that assumption. ## Looking for an Old CodeWalkers Contest? CodeWalkers previously ran PHP coding contests where readers' scripts competed against each other. This page is a starting point for practice, not a restored archive of those contest entries. The [PHP guide](/programming/php/) covers current PHP techniques if an old contest link brought you here. ## Does CodeWalkers Run Coding Contests Now? CodeWalkers does not currently run a recurring coding contest schedule. The exercises on this page are self-paced practice, with no submission deadline, ranking, or prize. There is no announced date for new contests. When you are ready to move from exercises to a project, the [programming learning paths](/programming/topics/learning-paths/) explain what to learn first and what to build. ## Frequently Asked Questions ### Are coding challenges the same as technical interview questions? Coding challenges and technical interview questions overlap, but they serve different purposes. Practice exercises help you learn; interview tasks help an employer assess your skills. Either can use automated tests, and interview formats vary. ### When should you look at a coding challenge solution? Read a hint for a coding challenge when you can explain what you have tried and where you are stuck. There is no fixed time limit for practice. After reading a solution, close it and try to reproduce the approach without copying. ### What is competitive programming? Competitive programming is a timed contest in which participants solve programming problems under stated rules. Automated judges commonly check correctness and execution limits. Scoring varies by contest, so speed is not the only factor in every event. ### Should you use AI to solve coding challenges? Use AI for coding challenge hints or explanations when you are practicing, but write and check your own solution if you want to learn the skill. Ask for one hint rather than a complete answer. In an assessment or contest, follow the rules on outside assistance. --- ## DevOps & Server Administration URL: /programming/topics/devops/ Cluster: Programming > Topics DevOps connects [application code](/programming/) with the systems that build, run, measure, and recover it. For a small web application, that work includes terminal access, process and proxy configuration, caching, observability, backups, and tested recovery steps. This hub routes each task to a focused guide. Start with the terminal guides if command-line work is new, then choose the server topic that matches the system in front of you. ## Start Here - [The Terminal: A Practical Guide](/programming/topics/devops/the-terminal/) teaches shell navigation, file operations, pipes, redirection, and safe command habits. - [Terminal Commands: A Practical Reference](/programming/topics/devops/terminal-commands/) provides task-organized examples and expected output for common shell commands. - [Using AI Coding Agents in the Terminal](/programming/topics/devops/terminal-ai-agents/) covers project scope, approvals, command review, and checking changes made by terminal agents. ## Choose a DevOps Guide - [Server Statistics and Observability](/programming/topics/devops/server-statistics/) explains metrics, logs, traces, browser timing, host pressure, and alerts tied to decisions. - [Server Contingency Planning](/programming/topics/devops/server-contingency-plan/) turns failure scenarios into owned restore, rollback, communication, and verification steps. - [Squid Caching Proxy](/programming/topics/devops/squid-caching-proxy/) covers proxy roles, access controls, cache behavior, and the cases where Squid still fits. ## Related Programming Guides - [Backend development](/programming/topics/backend/) explains the server-side code and data layer that DevOps keeps running. - [PHP programming](/programming/php/) covers the web applications behind many server-administration tasks. - [SQL programming](/programming/sql/) covers database design, queries, indexes, and transactions that affect operational health. - [APIs](/programming/topics/apis/) covers service contracts, retries, callbacks, and integration logging. ## Frequently Asked Questions ### What is the difference between DevOps and system administration? Traditional administration is a separate team keeping servers running after developers hand over finished code. DevOps merges the two responsibilities, so the people who build a system also help run, monitor, and repair it in production. ### Do you need Docker to learn DevOps basics? No. Terminal fluency, process management, logging, and recovery planning apply whether the application runs in a container, a virtual machine, or directly on a server. Containers are a deployment choice on top of those fundamentals. ### What is the difference between uptime and reliability? Uptime says whether a service is running. Reliability says whether it does its job correctly and consistently, so a server can show perfect uptime while timing out, failing requests, or returning wrong data. ### What does infrastructure as code mean? Defining servers, networks, and configuration in version-controlled files that a tool applies, rather than clicking through a control panel or running one-off commands. The environment can then be rebuilt or audited from the files themselves. ### Should logging live in the application or on the server? Both, for different reasons. Application logs capture business events and request context only the code knows. Server and process logs capture crashes, restarts, and resource limits the application cannot see from inside itself. --- ## Server Contingency Planning URL: /programming/topics/devops/server-contingency-plan/ Cluster: Programming > Topics A server contingency plan is a fire drill for a web service. It records the exits, equipment, owners, and checks before an emergency removes the time needed to work them out. The plan succeeds when a specific person can restore a safe service from the written steps. Backups, deploy tools, managed databases, and CDNs are parts of the response, but none is the complete plan. A usable plan connects failure impact to recovery targets, access, rollback or restore actions, communication, and evidence that the service is ready again. ## What a Server Contingency Plan Defines NIST contingency guidance starts with requirements and priorities rather than a list of products. For each service, define the tolerated outage, tolerated data loss, dependencies, recovery order, and conditions that make the recovered system acceptable. Two recovery targets shape the plan's technical choices. The recovery time objective (RTO) sets the target time to restore an acceptable service. The recovery point objective (RPO) sets the maximum acceptable period of lost data. A service with a fifteen-minute RPO needs more frequent protection than one that can lose a day of changes. ```yaml service: customer-portal rto: 4 hours rpo: 15 minutes incident_owner: operations-lead communications_owner: support-lead recovery_order: - database - application - background-jobs ``` This first record gives the plan a measurable boundary. Replace role names with the team's actual contact route, and keep sensitive credentials in an approved secret store instead of copying them into the document. ## Build the First Plan Start with the smallest service that would cause real harm if it failed. Record its code repository, deployment location, database, file storage, DNS and CDN controls, scheduled jobs, external providers, alert route, and access owners. Then describe the safest recovery path for each dependency. Keep the first version concrete and directly executable. A sentence such as "restore the database" hides the backup location, decryption key, target server, command, schema checks, and application validation. Name those details or link to a versioned runbook that does. ```yaml trigger: database writes fail for 5 minutes owner: database-on-call first_checks: - confirm failure from two locations - pause write-heavy background jobs - record the deploy and migration versions recovery: - choose rollback, failover, or restore proof: - create and read a test record - confirm queued jobs resume ``` The trigger prevents a vague dashboard warning from starting an improvised response. The proof prevents a healthy homepage from hiding a broken write path. ## Map Failures to Owners Separate complete outages from the service's partial failure modes. The application may serve cached pages while database writes fail, a queue may stop while requests remain fast, or a provider may reject payments while the rest of the site works. Each scenario needs a detection signal, decision owner, recovery action, and verification step. Record who can change DNS, purge a CDN, roll back a deployment, restore a database, rotate credentials, disable an integration, and approve reopening. Add a backup owner for every critical action. If one unavailable person holds the only account or recovery key, that access dependency belongs in the plan. CISA incident-response guidance also separates operational work from communication. A technical owner can coordinate recovery while another owner publishes status updates and records decisions. That split keeps user communication moving without interrupting the person running the recovery steps. ## Prove Backups with Restore Tests A backup is stored input for recovery. A restore test proves that the input is complete, readable, compatible with the current application, and available within the RTO. Test database data, uploaded files, deployment configuration, scheduled jobs, and any provider settings that cannot be recreated from code. Restore into an isolated environment so the drill cannot overwrite production or send real messages. Check record counts, schema version, recent sample records, file references, login behavior, and one important read and write flow. Record the time required for each recovery stage. A drill record for a worked example looks like this: ```text Worked example (hypothetical) Backup created: 02:00 Restore started: 09:10 Database available: 09:42 Application checks passed: 10:03 Example recovery time: 53 minutes Example recovery point: 02:00 ``` The example's recovery time and recovery point show which fields belong in a drill record. A real drill must record its own results, compare them with the targets, and identify the slowest or most uncertain step before the next exercise. ## Prepare Rollback and Bypass Paths Many incidents need a smaller reversal instead of a full restore. Prepare a normal deployment rollback, a forward database fix when migration rollback would lose data, a feature-disable path, a cache or proxy bypass, a credential-rotation procedure, and a way to pause unsafe background work. Write the order of operations before an incident begins. Rolling back application code while leaving a newer incompatible schema can extend the outage. Purging a cache before the origin is healthy can direct every request at the failing service. The runbook should identify dependencies and the check required before the next step. ## Run and Record a Recovery Drill Run the plan as a timed exercise with a clear scenario, such as a failed database migration or unavailable object-storage bucket. The operator should begin from the document and ordinary access process. Private notes and remembered commands stay out of the drill. During the drill, record missing permissions, stale contacts, ambiguous commands, unexpected dependencies, manual approval delays, and checks that did not prove enough. Update the plan immediately after the exercise finishes. Repeat the failed section until another operator can follow it without hidden context. Finish by confirming alert recovery, application reads and writes, background jobs, provider callbacks, and user-facing status. The service is recovered only when the important flows work and the owner has evidence for that decision. ## Common Pitfalls & Debugging ### The Plan Depends on One Person Symptom: the response stops because the only person with access or knowledge is unavailable. Cause: ownership and credentials were never tested as dependencies. Fix: assign a backup owner, verify access during each drill, and document the approved escalation route. ### The Backup Cannot Restore Symptom: the restore command fails, required files are missing, or the application cannot read the recovered schema. Cause: backup creation was monitored but restoration was not. Fix: test the complete restore in isolation and alert on both backup age and failed restore drills. ### Recovery Restores the Old Bug Symptom: the service returns with the same unsafe configuration or incompatible deployment. Cause: the plan restored data without matching code, schema, and configuration versions. Fix: record all three versions and validate compatibility before reopening traffic. ### The Service Returns Before It Is Safe Symptom: the homepage works, but writes, jobs, or provider callbacks still fail. Cause: recovery used one shallow uptime check. Fix: define read, write, queue, and integration checks that match the service's important user flows. ## Conclusion A useful server contingency plan turns recovery targets into owned, tested actions. Run it like a fire drill, record the observed result, and fix every step that depends on memory, missing access, or an untested backup. ## Frequently Asked Questions ### What is the difference between RTO and RPO? The recovery time objective (RTO) is the target time for restoring an acceptable service. The recovery point objective (RPO) is the maximum acceptable period of lost data. A four-hour RTO and fifteen-minute RPO require different recovery systems and tests. ### How often should a contingency plan be reviewed? Review the plan after infrastructure, provider, ownership, or data-flow changes and after every incident. Set a regular review at least once a year, then test the highest-risk restore and rollback paths more often according to the service's recovery objectives. ### Does a small website need a server contingency plan? A small website still needs a server contingency plan. The plan can fit on one page and record backups, access, rollback commands, service contacts, and recovery checks. Its size should match the service, but the owner should not have to reconstruct the recovery process during an outage. ### Where should a server contingency plan be stored? Store the plan in a version-controlled location that incident responders can reach when normal systems are unavailable. Keep an approved offline or secondary copy for critical access details. Reference secret-store locations and access procedures without placing credentials directly in the plan. ## Related DevOps Guides - Use [server statistics and observability](/programming/topics/devops/server-statistics/) to define the signals that start and end the response. - Read [the practical terminal guide](/programming/topics/devops/the-terminal/) before following command-line recovery steps. - Plan proxy bypasses with [the Squid caching proxy guide](/programming/topics/devops/squid-caching-proxy/). - Return to the [DevOps and server administration hub](/programming/topics/devops/) for the full topic path. --- ## Server Statistics and Observability URL: /programming/topics/devops/server-statistics/ Cluster: Programming > Topics Server statistics are the instrument panel for a web service. One gauge can show that the machine is busy, but a useful panel combines request traffic, errors, latency, resource pressure, and dependency behavior so an operator can decide what to check next. The old pattern of running `uptime` from a public PHP page exposes too little context and creates a risky shell boundary. Current observability collects metrics, logs, traces, and browser timing through narrow instrumentation, then connects each signal to a question or response. ## What Observability Measures Metrics record numerical measurements that change over time. Request count, response duration, error rate, CPU use, memory pressure, disk space, active database connections, cache hit ratio, and queue depth all fit this signal. Prometheus stores metrics as time series with names, timestamps, and optional labels. Logs record individual events together with operational context. A useful application log can include a timestamp, severity, service name, request ID, route, status code, deploy version, and a bounded error message. Keep credentials, session tokens, payment data, and unnecessary personal data out of the event. ```json { "level": "error", "service": "checkout", "request_id": "req_8414", "route": "/api/orders", "status": 503, "deploy": "2026.07.29.2" } ``` Traces connect the work performed for one request across boundaries. A trace can show the application waiting on SQL, an external provider, or a queue. OpenTelemetry defines traces, metrics, and logs as telemetry signals and provides shared conventions for carrying context between instrumented components. Each signal answers a different operational question during diagnosis. Metrics show when a pattern changed, logs explain individual events, and traces show where a request spent time. Shared request or trace identifiers let an operator move between them without guessing which events belong together. ## Build a Useful Server Baseline Begin with request rate, error rate, and latency for the service's important routes. Add deploy markers so a change in the panel can be compared with the code and configuration version that produced it. Then measure the dependencies that can slow or stop those routes, such as database connections, query duration, cache results, and queue backlog. Prometheus counters and histograms support this baseline. A counter can track completed requests by route and status class, while a histogram records observations in buckets that can be used to estimate latency percentiles. ```text 100 * ( sum by (route) ( rate(http_requests_total{status=~"5.."}[5m]) ) / sum by (route) ( rate(http_requests_total[5m]) ) ) histogram_quantile( 0.95, sum by (le, route) (rate(http_request_duration_seconds_bucket[5m])) ) ``` The first query divides server errors by all requests and multiplies the result by 100 to show the recent error percentage for each route. The second estimates p95 latency, the boundary below which roughly 95 percent of observations fall. Keep route labels bounded to templates such as `/orders/{id}`; a raw customer or order ID creates a new time series for every value. Interpret percentile measurements alongside the matching request counts. A low-traffic route can show a high p95 after one slow request, while a busy route can hide many affected users behind a small percentage. The panel should show enough volume to interpret the timing. ## Measure Dependencies That Control Throughput Application latency often begins in a dependency rather than the web process. A database panel should pair query duration with active and waiting connections, lock waits, transaction failures, and replication lag when replicas are part of the read path. Compare those measurements with the same route and deploy window shown in the request panel. Caches need hit, miss, eviction, error, and origin-request counts. A high hit ratio can still hide stale or incorrectly shared responses, so track cache bypass reasons and purge events too. Queues need depth, oldest-job age, processing duration, retries, and permanently failed jobs. Queue depth alone cannot distinguish healthy traffic growth from workers that stopped processing. ```text app_database_connections_active / app_database_connections_limit max(app_queue_oldest_job_age_seconds) sum(app_queue_failed_jobs_total) ``` These example metric names compare database use with its limit and expose queue age and failures. Place those results beside request signals on the instrument panel so an operator can identify dependency pressure before the host-level gauges reach their limits. ## Measure Host and Disk Pressure Host measurements explain whether the runtime has enough capacity to do its work. Track CPU saturation, available memory, swapping, disk bytes and inodes, network errors, open file descriptors, and process restarts. Database and container limits may fail before the whole host reaches a visible ceiling, so include their configured limits and current use. A private PHP health tool can inspect disk space without executing `df`. PHP provides `disk_free_space()` and `disk_total_space()` for a filesystem or disk partition. ```php $free, "used_percent" => round((($total - $free) / $total) * 100, 1), ]; } ``` Keep this output behind authentication and authorization, and treat it as one current reading. Host monitoring should retain the historical series and send the alert. Watch both available bytes and inodes because a filesystem can reject new files after either resource runs out. Set thresholds from observed growth and recovery time. An alert at 90 percent disk use may arrive too late if logs grow quickly and the person responding needs an hour. The instrument panel should warn while a safe action is still available. ## Connect Server and Browser Timing Server measurements stop before rendering, device work, and much of the network path. The `Server-Timing` response header can expose selected server durations to browser developer tools, which helps connect backend work with the request visible in the browser. ```php Topics Squid is a mature, cross-platform caching proxy. Choose it when the proxy itself is the requirement: explicit proxying, controlled egress, internal caching, access-control policy, and networks where the administrator wants to inspect and shape outbound HTTP traffic. For public-site caching, a CDN, a reverse proxy such as NGINX or Varnish, or a framework-level cache that understands the application usually fits better. A proxy cache can postpone a hardware purchase by making repeated traffic stop hitting the origin every time. That effect is real, and it is also easy to mistake "cache hit" for "the application is healthy." The cache can hide load, and it can also hide bugs. ## What Squid Is Squid is a proxy server with caching support. In a forward-proxy setup, clients send requests through Squid on their way to the internet or another network. In a reverse-proxy or accelerator setup, Squid sits in front of origin services and serves cached responses when the rules allow it. Those jobs look similar on a diagram, but they solve different operational problems. Forward proxying is about clients and policy: - which users or networks may make requests- which destinations are allowed or blocked- how traffic is logged- how authentication and access-control lists work- whether repeated external responses can be cached safely Reverse-proxy caching is about protecting the origin: - which responses can be reused- how cache keys are formed- how long content stays fresh- what happens when the origin is unavailable- how private, authenticated, or user-specific responses are kept out of shared cache Squid can participate in both patterns, but modern public web stacks often choose a CDN or NGINX-style reverse proxy for origin shielding. That shift does not make Squid obsolete; it narrows the reason to choose it. ## Cache Only What Is Safe to Reuse Caching is a contract between the application, the proxy, and the client. HTTP headers such as `Cache-Control`, `ETag`, and `Vary` are not decoration. They describe whether a response can be reused, for whom, and under which request conditions. Static assets are usually simple, while user-specific pages are where shared cache gets dangerous. Anything tied to a session, permission check, shopping cart, account page, admin view, or personalized API response deserves caution. For a small PHP site, this means the app needs to be clear about response type before a proxy is allowed to help: ```php Topics A terminal AI coding agent is a program you start inside a project directory so it can help with that project. You will learn to start an agent in the right project, supervise its commands, and review its changes. Before continuing, learn the basic navigation and command patterns in [The Terminal: A Practical Guide](/programming/topics/devops/the-terminal/) and the Git basics in the [official Git Documentation](https://git-scm.com/doc). Only a little Git is needed at this stage: recognize what a repository is, and read `git status` and `git diff` output, because agents show their proposed changes that way. Deeper Git knowledge can wait. Treat the agent like a contractor working inside your house. You choose which house it enters, explain the job, decide which rooms it may enter, and inspect the work before accepting it. A skilled contractor can still misunderstand a request or open the wrong door, so access and review remain your responsibility. ## What a Terminal Coding Agent Is A terminal coding agent combines a conversation with access to development tools. You start it from a shell, describe a task in ordinary language, and let it inspect the project files needed to understand the request. It can then propose file edits and shell commands, while its permission settings determine when it asks for approval. Widely used agents include Claude Code from Anthropic, Codex CLI from OpenAI, and opencode as an open source project. Their interfaces differ, but the beginner workflow stays recognizable: enter the project, start one agent, describe one focused task, and review what it proposes. ```text > Explain where this project renders its weather forecast. > Do not edit any files yet. ``` The `>` symbols mark text typed into the agent's chat, not shell redirection, so do not type them. This opening request gives the agent a narrow investigation and an explicit read-only boundary. It also gives you a chance to check whether the response names files that actually belong to the project. ## Start an Agent in the Right Project The working directory is the location from which a terminal program begins. A coding agent uses that location as the starting point for discovering project files, instructions, and version-control information. Starting one directory too high may expose unrelated projects, while starting inside a small subdirectory may hide files the task needs. Check the location and contents before launching the installed agent. The following example enters one project, prints its path, and lists its top-level files. Read the output and confirm it is the right project before starting the agent: ```bash cd "$HOME/projects/weather-dashboard" && pwd && ls ``` The `&&` operators stop the chain if `cd` fails, so `pwd` and `ls` do not run from the wrong location. The pause before launch lets you read the printed directory and file list. Before continuing, install Codex CLI from OpenAI's [official installation instructions](https://github.com/openai/codex). After confirming the output, start Codex CLI: ```bash codex ``` If Claude Code is the agent you want to try, install it from the [official install page](https://code.claude.com/docs), then run `claude`. For opencode, follow its [official installation instructions](https://github.com/anomalyco/opencode) before running `opencode`. After installing any of these tools, typing `codex`, `claude`, or `opencode` at the prompt starts it; use the matching `--version` command, such as `codex --version`, to confirm the installation without starting a session. ## Supervise an Agent Session Give the agent a small task with a visible finish line. "Add an empty message to the weather card when no forecast is available" is easier to review than "improve the dashboard." Name any limits that matter, such as the file to change, the behavior to preserve, or the checks to run. Before the first session, check the official documentation for [Claude Code](https://code.claude.com/docs), [Codex CLI](https://learn.chatgpt.com/docs/codex/cli), or [opencode](https://github.com/anomalyco/opencode), then configure its approval or permission settings. Keep confirmations enabled wherever the tool offers them. When the agent proposes a command, read the command itself before approving it. Check the program name, every option, and each path. A test command aimed at the current project is different from a removal command aimed at a parent directory. If part of the command is unfamiliar, ask the agent to explain it or decline it and inspect the relevant documentation. Use this quick comparison while reviewing a proposed command: | Safer Signal | Risky Signal | | Known project path | Parent or home path | | Read-only command | Delete or overwrite | | Explained options | Unfamiliar options | After the agent reports that the task is complete, review the working tree with Git: ```bash git status --short # M src/components/WeatherCard.tsx # ?? src/components/WeatherEmptyState.tsx git diff HEAD # Example diff output: # - return null; # + return

    Forecast unavailable.

    ; ``` The `git status --short` command lists tracked changes and marks new files with `??`. `git diff HEAD` shows the removed and added lines in tracked files; `HEAD` makes the diff include staged changes too. New files are not in that diff, so open them directly before accepting the work. If the agent changed an unrelated configuration file, ask why before continuing. If the explanation does not match the task, ask the agent to undo that specific change, then re-review the diff. One focused request followed by one diff review keeps the inspection small enough to do properly. Repeat the review after any correction or test failure. The agent's summary is a useful index, but the files and command output are the evidence. Your inspection is the contractor's final room-by-room check before the job is accepted. ## Set Boundaries Before Work Begins Start agents only in directories that contain the intended project. A home directory, desktop, or broad projects folder can include personal documents, credentials, backups, and unrelated repositories. The agent does not need access to those rooms for a focused coding task. Version control supplies checkpoints, a readable record of changes, and an undo path for committed work. Begin from a state you understand, check `git status` before the session, and review `git diff` before committing. Uncommitted work from another task makes it harder to separate the agent's edits from changes that were already present. Review commands especially carefully when they install software, remove files, change permissions, contact external services, or operate outside the project directory. - Open the smallest directory that contains the complete project. - Describe one focused task and its boundaries. - Read proposed commands before granting approval. - Inspect changed files and run relevant checks. - Commit only changes you can explain. These boundaries do not assume the agent will make a mistake. They make any mistake smaller, visible, and easier to reverse. ## Common Pitfalls & Debugging ### The Agent Started in the Wrong Directory Symptom: the agent discusses unrelated files or cannot find the expected project. Cause: it was launched from a parent, sibling, or nested directory. Fix: stop the session, run `pwd` and `ls`, enter the correct project with `cd`, then launch a new session there. ### A Command Was Approved Without Being Read Symptom: a command changes more than the task required or targets an unexpected path. Cause: approval was given from the description instead of the complete command. Fix: stop further work and ask the agent to undo that specific change. Re-review the diff. Use version control or a backup if the agent cannot undo it safely. ### The Changes Are Hard to Explain Symptom: the result appears to work, but you cannot explain several changed lines. Cause: the task was too broad or the review relied on the agent's summary. Fix: run `git diff` before continuing, ask about each unexpected change, and reduce the next request to one behavior or file. ### The Agent Command Is Not Found Symptom: the shell reports `claude: command not found`, `codex: command not found`, or `opencode: command not found`. Cause: the agent is not installed, the command name is misspelled, or its installation directory is missing from `PATH`. Fix: check the official installation guide and reopen the terminal if the installer changed your shell path. ### The Agent Appears Stuck Symptom: the terminal stops responding to ordinary input while the agent or one of its commands runs. Cause: a foreground process is still active, waiting, or taking longer than expected. Fix: read the latest output, then press Ctrl+C to interrupt the foreground process in the usual case. ## Conclusion A terminal coding agent is safest when you choose the right project, give it a narrow task, and inspect its commands and file changes. Keep the contractor inside the intended rooms, then accept the work only when you can explain the diff. ## Frequently Asked Questions ### Do you need to know how to code to use a terminal agent? You can ask an agent to explain an unfamiliar project without knowing much code, but basic programming knowledge helps you judge its edits and spot incorrect assumptions. Start with small, reversible tasks and read every change instead of treating the agent's answer as proof. ### Which terminal agent should you start with? If your team already uses one, use that, because shared conventions matter more than any feature difference. Working alone, check what you are already paying for first. Running any of these costs something in model usage, but how you pay differs: some are tied to a vendor subscription, while opencode is open source and runs against whichever provider key or local model you point it at. An assistant plan you already hold is usually the cheapest way in. If none of that applies, the choice matters less than it looks. Claude Code, Codex CLI, and opencode all launch from a project directory with their own terminal command, so every habit on this page transfers: check where you started it, supervise the commands, review the diff. Pick one, learn the shape, and switch later if you need to. For a direct feature comparison, see [Claude Code vs Codex](/ai-coding-tools/comparisons/claude-code-vs-codex/) and [Claude Code vs opencode](/ai-coding-tools/comparisons/claude-code-vs-opencode/). ### Can a terminal agent break your system? A terminal agent can break your system by running a harmful command or editing the wrong file when it has sufficient access. Limit the working directory, keep important work in version control, read proposed commands, and avoid confirmation-skipping modes until you understand their permissions. ### Do terminal agents work on Windows? Terminal agents work on Windows, although installation and shell behavior depend on the tool. Windows Subsystem for Linux (WSL) provides a Linux terminal where common Bash commands, project paths, and agent workflows closely match the macOS and Linux examples on this page. ### Can a terminal agent read files outside the project you started it in? It depends on the tool and the permissions you grant, and several can. The working directory is the default scope rather than a hard boundary, which is why starting the agent in the right project, and not in your home directory, matters. ### What does an agent's auto-approve mode actually turn off? Less than the name suggests, and the answer differs by tool. There is no shared permission model across terminal agents, so check your own tool's documentation rather than assuming the behavior carries across. Most of them keep two separate controls: what the agent is allowed to touch, and whether it asks before each step. Turning off the asking does not widen the first. Codex CLI selects a sandbox policy with `--sandbox` independently of its approval setting, and reserves a separate `--dangerously-bypass-approvals-and-sandbox` flag for dropping both at once. Claude Code likewise separates `--permission-mode` and its per-tool allow and deny lists from the `--dangerously-skip-permissions` escape hatch. The useful question is which boundary still holds while auto-approve is on. Confirm that before running an agent anywhere you cannot afford to lose, and stay in a scratch checkout while you are still learning a tool's defaults. ### How is a terminal agent different from AI autocomplete in an editor? Autocomplete suggests the next lines while you type and you accept or reject each one. A terminal agent takes a goal, then reads files, edits several, and runs commands on its own. One assists your typing; the other acts. ## Self-Check 1. Which pair checks the current project before an agent starts: (A) `pwd` and `ls`, (B) `cp` and `mv`, or (C) `grep` and `sort`? 2. Why does the location-check command place `&&` between `cd`, `pwd`, and `ls`? 3. Predict the output: the agent edits one tracked file named `src/app.ts` without staging it. What does `git status --short` print? 4. What should you do when a proposed command contains an unfamiliar option or path? 5. What should you check before the first session when a tool offers approval or permission settings? ### Answers 1. A: `pwd` and `ls`. They show the current directory and its contents before the agent receives project access. 2. Each command runs only after the previous one succeeds. A failed directory change stops the remaining location checks. 3. ` M src/app.ts`. The `M` in the second status column marks a tracked file with unstaged changes. 4. Decline or pause the command and ask for an explanation. Approve it only after its program, options, and targets are clear. 5. Check the tool's official documentation and configure the settings. Keep confirmations enabled wherever the tool provides them. ## Related DevOps Guides - Learn the prerequisite commands in [The Terminal: A Practical Guide](/programming/topics/devops/the-terminal/). - Look up command syntax and expected output in [Terminal Commands: A Practical Reference](/programming/topics/devops/terminal-commands/). - Return to the [DevOps and server administration hub](/programming/topics/devops/) for the cluster overview. - Use [server statistics and observability](/programming/topics/devops/server-statistics/) to understand the systems an agent may inspect. - Build recovery habits with [server contingency planning](/programming/topics/devops/server-contingency-plan/). --- ## Terminal Commands: A Practical Reference URL: /programming/topics/devops/terminal-commands/ Cluster: Programming > Topics This reference groups common terminal commands by job, so you can find the right one without scanning an alphabetical list. Use [The Terminal: A Practical Guide](/programming/topics/devops/the-terminal/) to learn the underlying shell concepts, then use [Using AI Coding Agents in the Terminal](/programming/topics/devops/terminal-ai-agents/) to apply those commands while supervising an agent. Each entry gives the command's job, a minimal example, and the expected result. ## Navigation Start here whenever a command depends on where you are. Relative paths begin in the working directory, so confirm that location before changing files. An absolute path starts at the filesystem root, a relative path starts at the working directory, and `~` represents your home directory. The special path `.` means the current directory, while `..` means its parent. ### `pwd`: Print the Working Directory `pwd` prints the full path of the directory the shell is currently using. ```bash pwd # /Users/alex/projects/weather-app ``` ### `ls`: List Directory Contents `ls` lists visible files and directories in the current location, while `ls -la` includes hidden entries and details. ```bash ls # README.md src tests ``` ### `cd`: Change the Working Directory `cd` enters another directory; `cd ..` moves to its parent, and `cd` without a path returns home. ```bash cd src pwd # /Users/alex/projects/weather-app/src ``` ## Files & Directories File commands normally accept a source path, a destination path, or both. Run `pwd` and `ls` first when the target is unclear. `cp` and `mv` can replace an existing destination, depending on the command and options. On some systems, adding `-i` makes the command ask before overwriting. Read `man cp` or `man mv` before moving a directory tree; `man` is explained in the [Environment & Help](#environment-and-help) section below. ### `mkdir`: Create a Directory `mkdir` creates a directory; `-p` also creates missing parent directories and does not fail when the path already exists. ```bash mkdir -p reports/daily ls reports # daily ``` ### `cp`: Copy a File `cp` copies the source to the destination and leaves the original in place. ```bash cp report.txt report-backup.txt ls report* # report-backup.txt report.txt ``` ### `mv`: Move or Rename a File `mv` moves a file to another directory or gives it a new name. ```bash mv draft.txt final.txt ls # final.txt ``` ### `rm`: Remove a File `rm` removes a named file without sending it to the desktop Trash. Successful removal is silent, so list the directory to confirm the result. ```bash rm old-report.txt ls old-report.txt # ls: old-report.txt: No such file or directory ``` Removing a directory and its contents requires recursive behavior such as `rm -r`. That option expands the amount of data affected, so verify the complete path and avoid adding force options merely to silence an error. ### `touch`: Create or Update a File `touch` creates an empty file when it does not exist, or updates an existing file's modification time. ```bash touch notes.txt ls notes.txt # notes.txt ``` ## Reading & Searching Text These commands inspect text without opening an editor. Choose one based on file length and whether you need every line, one end, or matching lines. `head` and `tail` print ten lines by default. Set an explicit count when a script or explanation depends on a fixed result, and use `less` when the useful part lies between the two ends. ### `cat`: Print a Short File `cat` writes an entire file to standard output, which makes it useful for short files. ```bash cat status.txt # deployment ready ``` ### `less`: Read a Long File `less` opens a scrollable viewer for long files. Use the arrow keys or Page Up and Page Down, then press `q` to return to the prompt. ```bash less server.log # Opens server.log in the less viewer; press q to exit ``` Inside `less`, type `/` followed by text and press Return to search forward. After a match appears, press `n` to move to the next one. The file is only being viewed, so leaving `less` does not change it. ### `head`: Read the First Lines `head` prints the beginning of a file; `-n 2` requests exactly two lines. ```bash head -n 2 servers.txt # web-01 # web-02 ``` ### `tail`: Read the Last Lines `tail` prints the end of a file. Add `-f` to keep watching as a log grows, then press Ctrl+C to stop following it. ```bash tail -n 2 deployment.log # INFO health check passed # INFO deployment complete ``` ### `grep`: Find Matching Lines `grep` prints lines containing a pattern; `-i` makes a text match case-insensitive. ```bash grep "ERROR" deployment.log # ERROR disk full # ERROR backup failed ``` ## Pipes & Redirection Pipes and redirection control where output goes. A pipe hands output to another command, while redirection sends it to a file. Shell programs normally begin with three streams: standard input, standard output, and standard error. Their numeric descriptors are `0`, `1`, and `2`. Ordinary `>` redirects standard output, while `2>` selects the error stream. ### `|`: Send Output to Another Command The pipe operator sends standard output from the command on its left into standard input for the command on its right. ```bash cat deployment.log | grep "ERROR" # ERROR disk full # ERROR backup failed ``` ### `>`: Write Output to a File `>` writes standard output to a file, creating it or replacing all existing contents. ```bash echo "ready" > status.txt cat status.txt # ready ``` ### `>>`: Append Output to a File `>>` appends standard output to the end of a file without deleting the text already there. ```bash echo "checked" >> status.txt cat status.txt # ready # checked ``` ### `2>`: Redirect Error Output `2>` sends standard error to a file while leaving ordinary standard output unchanged. The exact error wording shown by this example varies between operating systems. ```bash ls missing-folder 2> errors.txt cat errors.txt # ls: missing-folder: No such file or directory ``` ## Processes This section is optional for beginners. The everyday takeaways are that `ps` lists what is running, `kill` stops a background command, and Ctrl+C stops the one in front of you. Each running program is represented by at least one operating-system process. These commands help you see shell activity, stop a foreground task, or send a termination request to a process you started. A process ID identifies an operating-system process. A job number such as `%1` identifies work tracked by the current shell. Job numbers disappear with that shell session, while process IDs are visible to process tools across terminals when permissions allow. ### `ps`: List Processes `ps` reports a snapshot of the running processes visible to the current user. In this command, `$$` is the current shell's ID, and `-o pid=,comm=` limits the output to the process ID and command-name columns. The number will differ, macOS may print the shell as a full path such as `/bin/zsh`, and the name column's format varies between systems. ```bash ps -o pid=,comm= -p $$ # 48312 zsh ``` ### `kill`: Send a Signal to a Process `kill` sends a signal to a process. With no signal option, it requests normal termination rather than forcing an immediate stop. In this example, `&` runs `sleep` in the background, and `$!` holds the last background command's process ID. ```bash sleep 60 & process_id=$! kill "$process_id" # kill prints nothing itself ``` `kill` prints nothing itself; the shell may report the job as terminated at the next prompt. Give a process time to shut down cleanly before considering a stronger signal. A forced signal can prevent cleanup work, so it is a diagnostic last step rather than the default form of `kill`. ### Ctrl+C: Interrupt the Foreground Process Ctrl+C usually sends an interrupt signal to the command currently controlling the terminal and returns you to the prompt. ```bash sleep 60 # Press Ctrl+C # ^C ``` ### `jobs`: List Shell Jobs `jobs` lists tasks started from the current shell with job numbers and states. The job number can be used as a target such as `%1`. ```bash sleep 60 & jobs # [1]+ Running sleep 60 & kill %1 ``` The bracketed job number and status format differs slightly between bash and zsh. ## Environment & Help Environment variables store values used by the shell and launched programs. Help commands explain how the local version of a command interprets its options. `PATH` is a colon-separated list of directories searched for executable commands. Use lookup commands to inspect the current result before changing `PATH`. The local manual remains the best match for the exact command version installed on the computer. ### `echo $VAR`: Print an Environment Variable Use `echo "$VAR"` to print the value stored in a selected environment variable. Keep the reference in quotes so spaces remain part of one value. ```bash echo "$HOME" # /Users/alex ``` ### `which`: Locate a Command `command -v` is the portable POSIX way to determine how a command name resolves. In zsh, `which` is a built-in that also reports shell built-ins and aliases, while Bash usually resolves `which` to an external binary. ```bash which grep # /usr/bin/grep ``` ### `command -v`: Show How a Command Resolves `command -v` is a shell-oriented lookup that can identify executables and shell built-ins. ```bash command -v cd # cd ``` ### `man`: Open a Manual Page `man` opens the installed manual for a command. Read its synopsis and options, then press `q` to exit. ```bash man grep # Opens the local grep manual page; press q to exit ``` ### `--help`: Request Short Help Many commands accept `--help` after the command name and print a short usage summary. Some macOS tools omit this GNU-style option, so use `man` when it is rejected. ```bash grep --help ``` ## Common Pitfalls & Debugging The [terminal practical guide](/programming/topics/devops/the-terminal/) explains paths, shell behavior, and safe command habits in more depth. ### Quick Diagnostic: A Removed File Is Missing Symptom: a file removed with `rm` is absent from the desktop Trash. Cause: `rm` removes the directory entry directly. Fix: check `pwd`, list the exact target, and keep backups or version control before removal. For the deeper safety explanation, see [Removed Files Bypass the Trash](/programming/topics/devops/the-terminal/#rm-does-not-use-the-trash) in the terminal practical guide. ### Quick Diagnostic: A Spaced Path Splits Symptom: a command reports extra arguments or missing paths. Cause: the shell splits an unquoted path at each space. Fix: wrap the complete path in straight quotes, as in `cd "$HOME/project files"`. For more examples, see [Spaces in Paths Need Quoting](/programming/topics/devops/the-terminal/#spaces-in-paths-need-quoting) in the terminal practical guide. ### Quick Diagnostic: A Command Will Not Resolve Symptom: the shell rejects a requested name with `command not found`. Cause: the name is misspelled, the program is absent, or its directory is outside `PATH`. Fix: check the spelling and run `command -v name` before editing shell configuration. For the deeper lookup explanation, see [Command Not Found Points to PATH](/programming/topics/devops/the-terminal/#command-not-found-points-to-path) in the terminal practical guide. ### Quick Diagnostic: Filename Case Mismatch Symptom: a path works on one computer but fails on another. Cause: filename case rules differ across filesystems. Fix: match every letter's case exactly so commands and scripts behave consistently on macOS and Linux. For the cross-system explanation, see [Filename Case Can Change Between Systems](/programming/topics/devops/the-terminal/#filename-case-can-change-between-systems) in the terminal practical guide. ## Frequently Asked Questions ### What is the difference between > and >>? The > operator creates or replaces the destination file with new standard output. The >> operator appends new output to the end of the destination while keeping its existing contents. Check the target before using either operator. ### How can you check what a command will do before running it? Open its manual page with man command-name or request a short summary with command-name --help when the program supports that option. Read the synopsis, options, and target paths before running a command that changes or removes files. ### Do these commands behave differently in bash and zsh? Most examples behave the same in bash and zsh, but built-ins, startup files, job-status messages, and command lookup can differ. When output or behavior changes, check the shell you are running and use its local help or manual. ### Can you use these commands on Windows? These examples target Unix-like shells rather than Command Prompt or PowerShell. Windows Subsystem for Linux provides a Linux environment where the same Bash commands and path conventions work. Native PowerShell uses different commands and syntax for several tasks. ### Does grep support regular expressions, or only plain text? Both. Plain text matching is the default, -E enables extended regular expressions for pattern matching, and -F forces the pattern to be treated as fixed text even if it contains characters that would otherwise mean something in a regex. ### Can more than two commands be chained together with pipes? Yes. Any number of commands can be chained with additional pipe operators, and each command's output feeds directly into the next command's input in sequence, from left to right. ### What does the bracketed number before a job's status in jobs output mean? It is the job's ID within that shell session, referenced as %1, %2, and so on with kill or fg. A plus sign marks the current job, the one a bare fg or bg command acts on without an argument, and a minus sign marks the previous job. ### Does closing the terminal window stop a background job started with an ampersand? Usually yes. Closing the terminal normally ends jobs tied to that session unless the process was started with a tool such as nohup or disown, or is running inside a persistent session like tmux or screen. ## Related Terminal and DevOps Guides - Learn the shell concepts behind this reference in [The Terminal: A Practical Guide](/programming/topics/devops/the-terminal/). - Apply these commands while reviewing approvals and changes in [Using AI Coding Agents in the Terminal](/programming/topics/devops/terminal-ai-agents/). - Return to the [DevOps and server administration hub](/programming/topics/devops/) for server operations, observability, and recovery guides. --- ## The Terminal: A Practical Guide URL: /programming/topics/devops/the-terminal/ Cluster: Programming > Topics The terminal gives you a text interface to the operating system. A graphical app works like a front desk that offers prepared forms and buttons, while the terminal gives you a direct phone line where precise requests receive precise responses. On macOS, open Spotlight, search for Terminal, and press Return. On Linux, open Terminal from the applications menu. On either system, type a command at the prompt, then press Return to run it. Windows needs one setup pass first: [install WSL](https://learn.microsoft.com/en-us/windows/wsl/install). 1. Search for PowerShell in the Start menu, right-click it, and choose Run as administrator , because the install needs elevated permissions. 2. Run `wsl --install`, then restart your computer. 3. After the restart, search for Ubuntu or WSL in the Start menu and open it to reach the Bash prompt used in these examples. 4. The first start asks you to create a Linux username and password; this is expected, not an error. `mkdir` creates a folder, the `-p` option also creates any missing parent folders (dash-letter additions like `-p` are called options), and `$HOME` is your personal folder. ```bash mkdir -p "$HOME/terminal-practice" cd "$HOME/terminal-practice" pwd # /Users/your-name/terminal-practice ``` Lines starting with `#` in these examples show what the terminal prints back, so do not type them. On Linux, the final path usually begins with `/home` instead of `/Users`. The commands create a safe practice directory, enter it, and print its full location. Keep using this directory as the examples add files and folders. ## What the Terminal and Shell Do A terminal is the application that shows a prompt, accepts keyboard input, and displays output. Apple's Terminal application on macOS is one familiar example. Other terminal applications provide different tabs, colors, fonts, and shortcuts while doing the same basic job. The shell is the command interpreter running inside the terminal. It reads a command, expands variables such as `$HOME`, finds the requested program, connects its input and output, and reports the result. macOS uses zsh as its default shell. Bash remains common across Linux distributions, although users can choose another shell. The terminal carries text between you and the shell. The shell interprets each request and routes it to commands such as `ls`, `cp`, or `grep`. ## How to Read the Prompt The prompt marks the point where the shell is ready for another command. Its exact appearance depends on the shell and configuration, but it often includes a username, computer name, current directory, and final prompt symbol. ```text alex@laptop terminal-practice % ``` In this prompt, `alex@laptop` identifies the user and computer, while `terminal-practice` is the current directory. The percent sign is common in zsh prompts, while Bash prompts often end with a dollar sign. A root or administrator shell commonly ends with a hash sign. Do not copy the prompt text when a tutorial shows commands prefixed with `$` or `%`; type only the command after it. ## Navigate Through Directories Terminal navigation begins with three commands you will use constantly. `pwd` prints the working directory, `ls` lists its contents, and `cd` changes the working directory. A relative path starts from your current location, while an absolute path starts from the filesystem root. ```bash mkdir -p "$HOME/terminal-practice/reports" cd "$HOME/terminal-practice" pwd # /Users/your-name/terminal-practice ls # reports cd reports pwd # /Users/your-name/terminal-practice/reports cd .. pwd # /Users/your-name/terminal-practice ``` The first `pwd` prints the practice directory, `ls` shows the new `reports` folder, and the later `pwd` commands confirm each move into and out of that folder. The two dots in `cd ..` mean the parent directory. A single dot means the current directory, and `cd` without a path returns to your home directory. Run `pwd` before a file operation whenever the prompt does not make your location obvious. ## Create, Copy, Move, and Remove Files File operations follow the same path rules. `mkdir` creates a directory, `cp` copies a file, `mv` moves or renames it, and `rm` removes it. The next sequence creates a note, copies it, renames the copy, then removes only that copy. `printf` writes the text, and `>` sends it into the file; redirection is covered fully below. ```bash cd "$HOME/terminal-practice" mkdir -p drafts printf "Deploy at 10:00\n" > drafts/deploy-note.txt cp drafts/deploy-note.txt deploy-note-copy.txt mv deploy-note-copy.txt final-deploy-note.txt ls # drafts final-deploy-note.txt reports rm final-deploy-note.txt ls # drafts reports ``` The second listing confirms that `final-deploy-note.txt` was removed. The original file still remains at `drafts/deploy-note.txt` after this sequence finishes. Both `cp` and `mv` take a source followed by a destination, so read them as "copy this to there" and "move this to there." That order matters when two paths look similar. ## Read and Search Text `cat` writes a file to standard output, which is normally the terminal. That suits short files. `less` opens a scrollable viewer for longer files, and `grep` prints lines that match a pattern. ```bash cd "$HOME/terminal-practice" printf "INFO server started\nERROR disk full\nINFO retrying\nERROR backup failed\n" > deployment.log cat deployment.log # INFO server started # ERROR disk full # INFO retrying # ERROR backup failed grep "ERROR" deployment.log # ERROR disk full # ERROR backup failed less deployment.log ``` The first four output lines come from `cat`, and the next two are the matches from `grep`. The `less` viewer then opens the file without loading it into an editor. Press the arrow keys to move and `q` to return to the prompt. ## Connect Commands with Pipes and Redirection Every command can receive standard input and produce standard output. A pipe passes one command's output to the next, so each command handles one part of the request. Redirection sends output to a named file: `>` creates or replaces the file, while `>>` appends without removing existing content. ```bash grep "ERROR" deployment.log | sort > errors.txt printf "ERROR network timeout\n" >> errors.txt cat errors.txt # ERROR backup failed # ERROR disk full # ERROR network timeout wc -l < errors.txt # 3 ``` The worked chain searches the log, sends both matching lines through `sort`, and writes the alphabetized result to `errors.txt`. The next command appends another error without replacing the first two. Finally, `wc -l` receives the file as standard input and reports three lines. Check the destination before using `>`, because an existing file is truncated before the command writes new output. Use `>>` only when repeated runs should keep earlier content. ## Why the Terminal Matters for AI Coding Agents Command-line AI coding agents operate from a project directory, so terminal basics help you set their scope and inspect the commands shown on screen. Claude Code, Codex CLI, and opencode are three widely used agents launched from a terminal, and new terminal agents keep arriving because the terminal gives them direct access to files, commands, and version control. ```bash cd "$HOME/projects/weather-dashboard" && pwd && ls ``` You do not need to memorize every shell command first. Recognizing the current directory, reading command output, and checking whether an operation targets the intended path lets you review what appears in the terminal. In this example, `pwd` and `ls` confirm the project in scope before the agent starts. The full supervision workflow for approvals, reviewing changes, and safety boundaries lives in the dedicated [terminal AI agents guide](/programming/topics/devops/terminal-ai-agents/). ## Common Pitfalls & Debugging ### Removed Files Bypass the Trash Symptom: a removed file does not appear in the desktop Trash. Cause: `rm` unlinks files directly instead of moving them into a recovery folder. Fix: run `pwd` and `ls` first, name the exact target, and keep version control or backups for anything that matters. Avoid recursive removal until you understand every path involved. ### Spaces in Paths Need Quoting Symptom: `cd` reports too many arguments or a command treats one path as several inputs. Cause: the shell splits unquoted spaces into separate words. Fix: wrap the complete path in straight double quotes. ```bash mkdir -p "$HOME/terminal practice" cd "$HOME/terminal practice" pwd # /Users/your-name/terminal practice ``` ### Command Not Found Points to PATH Symptom: the shell reports `command not found` for a name you expected to run. Cause: the name is misspelled, the program is not installed, or its directory is absent from `PATH`, the list of directories the shell searches for commands. Fix: check the spelling, use `command -v` to locate known commands, and inspect the search path before changing shell configuration. ```bash command -v grep # /usr/bin/grep echo "$PATH" # /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin ``` ### Administrator Permissions Need Extra Caution Symptom: a command asks for a password or changes protected system files. Cause: `sudo` runs an authorized command with elevated permissions. Fix: stop and read the complete command before entering a password. Do not add `sudo` merely because an installation or file command failed. ### Filename Case Can Change Between Systems Symptom: a path works on one computer and fails on another. Cause: Linux filesystems are commonly case-sensitive, while the default macOS volume is usually case-insensitive but case-preserving. Fix: match filename case exactly in commands, imports, and scripts so the same path works in both environments. ## Conclusion Terminal work becomes manageable when each command has a clear location, input, and output. Practice in the dedicated directory until navigation, file operations, pipes, and redirection feel predictable. Those habits also make command-line coding agents easier to supervise. ## Frequently Asked Questions ### What is the difference between a terminal and a shell? The terminal is the application that displays text input and output. The shell is the program running inside it that reads commands, expands paths and variables, launches other programs, and returns their output to the terminal. ### Are terminal commands the same on Mac and Linux? Many navigation and file commands are shared because macOS and Linux are Unix-like systems, but options and implementations can differ. PowerShell uses a different command set on Windows. WSL provides a Linux environment where Bash and common GNU/Linux commands work directly. ### Do you need the terminal to use AI coding tools? No. Some AI coding tools have desktop or editor interfaces. Terminal basics let you start command-line tools such as Claude Code and Codex CLI from the intended project directory, read their output, and check paths before running commands. ### How do you recover from a wrong terminal command? Press the up-arrow to inspect the last command or run history to review earlier commands. Ctrl+C usually interrupts the foreground command, although some programs can catch or ignore it. Check the command and its output before deciding what to do next. ### Does tab-completion work the same way for commands as it does for file paths? Tab completion behaves differently depending on position: at the start of a line the shell completes command names from PATH, while after a command it completes file and directory names from the current location. Both stop at the first ambiguous character and expand further only once the remaining text is unique. ## Self-Check 1. Which macOS tool can open Terminal quickly: Spotlight, Finder Tags, Disk Utility, or Preview? 2. What does the `pwd` command print? 3. What does `printf "ERROR two\nINFO one\nERROR one\n" | grep "ERROR" | sort` print? 4. Which operator appends output without replacing the existing file: `>`, `>>`, `|`, or `<`? 5. Which command shows earlier shell commands so you can inspect one before running it again? ### Answers 1. Spotlight. Search for Terminal there, then press Return to open it. 2. The full path of the working directory. It confirms where relative paths and file commands will begin. 3. `ERROR one`, then `ERROR two`. `grep` keeps the matching lines and `sort` orders them alphabetically. 4. `>>`. A single `>` replaces the destination, while two angle brackets append to it. 5. `history`. It prints earlier commands so you can review their paths and arguments before reuse. ## Related DevOps Guides - Return to the [DevOps and server administration hub](/programming/topics/devops/) for the cluster overview. - Continue with [using AI coding agents in the terminal](/programming/topics/devops/terminal-ai-agents/) to practice safe project scope, approvals, and change review. - Make [Terminal Commands: A Practical Reference](/programming/topics/devops/terminal-commands/) your next step for beginner-friendly commands, examples, and expected output. - Use [server statistics and observability](/programming/topics/devops/server-statistics/) to connect command output with production signals. - Build recovery steps with [server contingency planning](/programming/topics/devops/server-contingency-plan/). --- ## Frontend Development URL: /programming/topics/frontend/ Cluster: Programming > Topics Frontend development covers everything that runs in the browser, from page structure and styling through to interactive behavior. If you are new to programming and still working out where each language fits, the guides below run in the order they build on each other, so you can start at the top and follow the links down as each piece clicks. ## What Frontend Actually Means On a typical day, frontend work is a mix of a few separate jobs that all happen on the same page. You mark up the content so a browser, and a screen reader, knows what it is looking at. You style that markup so it looks right on a laptop and on a phone. Then you wire up the parts that need to react to something a visitor does, like clicking a button or opening a menu. | Job | Language | Example | | Structure the content | HTML | Headings, a list, a signup form | | Style the layout | CSS | Colors, spacing, a responsive grid | | Add interactivity | JavaScript | A button that opens a menu | Many interactive pages use all three, but HTML and CSS can deliver a useful page on their own. A contact form can use `type="email"` for native browser validation and submit without JavaScript. Add JavaScript when it improves the experience, such as showing richer feedback without replacing the form's semantic foundation. ## Frontend Languages - [HTML](/programming/html/) gives a page its structure and meaning, and it is the first language to learn because every other frontend skill assumes a page already exists to style or script. - [CSS](/programming/css/) controls layout, color, spacing, typography, and responsive behavior once that structure is in place, answering how something should look rather than what it is. - [HTML and CSS](/programming/html-css/) walks through how the two languages divide that work on a real page, and which one to reach for first when a layout problem shows up. - [JavaScript](/programming/javascript/) adds behavior, the part of a page that reacts to a click and updates the screen without a full reload, and the wider frontend ecosystem is built on top of it. - [TypeScript](/programming/typescript/) layers a type system on top of JavaScript for larger codebases, catching a mismatched function call before the code ever runs. ## Learning Order Structure has to exist before you can style it, and something has to be on the screen before a script can grab hold of it, so HTML, CSS, and JavaScript get learned in roughly that order because each layer depends on the one before it. Learn enough HTML to build a page with headings, a list, a link, and a form, then move on to CSS once plain unstyled boxes start bothering you. You do not need to memorize every CSS property before touching JavaScript, a working feel for selectors and the box model is enough to get started. Add JavaScript once you want the page to react to something rather than just sit there, and leave TypeScript until JavaScript itself feels comfortable, because TypeScript's extra syntax only pays off once you already trust the language underneath it. None of this is a strict gate you have to clear before moving forward. Plenty of beginners bounce between an HTML page and its CSS for weeks before JavaScript enters the picture at all, adding a heading here and fixing a spacing bug there, and that back-and-forth is a normal way to learn a page instead of a sign you have fallen behind some imagined schedule. ## What to Build First Skip the tutorial-after-tutorial trap and build something real early, even if the result looks rough. Start with a personal bio page in [HTML](/programming/html/) and [CSS](/programming/css/) alone, no JavaScript yet. That constraint forces you to practice structure and layout without a script bailing you out when the markup is wrong underneath it. Once that page holds up on both a laptop screen and a phone, add one JavaScript feature: a button that expands your work history, or a toggle that switches a color theme. Building one small interaction yourself makes the relationship between the markup, styles, and script clearer than reading another disconnected example. After that, build something with state that changes while you use it. A to-do list is the classic choice because it forces you to add items, remove items, and keep the page in sync with a plain JavaScript array. Do it without a framework first. Once you have felt the tedium of manually updating the DOM every time that list changes, a framework's whole reason for existing finally clicks. ## Where Frameworks Fit Tools such as React, Next.js, SvelteKit, SolidJS, and Astro build on [JavaScript](/programming/javascript/) and TypeScript rather than replacing them, but they do different jobs. React and SolidJS focus on component UI; Next.js and SvelteKit add application and routing conventions; Astro renders content-focused pages with optional interactive components. These tools can provide components, build tooling, routing, data-loading patterns, or client-side navigation, depending on which one you choose. They do not all include the same router or replace full-page navigation by default. Learn the language fundamentals first so you can tell which abstraction is helping and which layer produced an error. ## Common Gotchas A few things catch nearly every beginner in the first month, and none of them mean you are bad at this. Your script can run before the page it is trying to control exists. Put a classic `