Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
157545 stories
·
33 followers

How to Containerize a Node.js Application with Docker and Deploy with GitHub Actions

1 Share

If you've been building Node.js projects, you've probably had an experience like this. The project runs fine on your machine, but when you push it to a server, something breaks.

Maybe it's a different Node version, maybe an environment variable is missing, or maybe a system dependency doesn't match. You spend an hour debugging something that was never actually a code problem.

Docker fixes this at the root. With Docker, you stop shipping just code. The Node version, dependencies, and config all travel inside the container. Your laptop, a CI server, a production VM — it behaves the same on all of them. No more environment surprises.

In this tutorial, we'll go through all this step by step: a multi-stage Dockerfile, using Docker Compose with PostgreSQL for local development, and a GitHub Actions workflow that pushes a fresh image to Docker Hub on every merge to main.

The complete code for this tutorial is available on GitHub.

Table of Contents

  1. Prerequisites

  2. The Sample Application

  3. Writing the Dockerfile

  4. The .dockerignore File

  5. The .gitignore File

  6. Build and Test the Image Locally

  7. Docker Compose for Local Development

  8. Automate the Build with GitHub Actions

  9. Deploying the Image

  10. Wrapping Up

Prerequisites

  • Node.js 18+

  • Docker Desktop, which you can download at docs.docker.com/get-docker. Windows users need WSL 2 before Docker starts. Open PowerShell as Administrator and run wsl --install. After the restart, Docker Desktop will install without issues.

  • A GitHub account

  • A Docker Hub account (free at hub.docker.com)

  • Some Express.js experience helps, but isn't required

The Sample Application

We're building a task management API with Express and PostgreSQL. Keep in mind the app is just a vehicle to teach you how this works. The Dockerfile and pipeline we set up here work the same way for any Node.js project.

Create the project:

mkdir nodejs-docker-cicd && cd nodejs-docker-cicd
npm init -y
npm install express pg dotenv
npm install --save-dev nodemon

Create src/index.js:

const express = require('express');
const { Pool } = require('pg');
require('dotenv').config();

const app = express();
app.use(express.json());

const pool = new Pool({
  host: process.env.DB_HOST,
  port: process.env.DB_PORT,
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
});

// Create table on startup
pool.query(`
  CREATE TABLE IF NOT EXISTS tasks (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    completed BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP DEFAULT NOW()
  )
`).catch(console.error);

// Health check — required for Docker HEALTHCHECK and load balancers
app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

app.get('/tasks', async (req, res) => {
  try {
    const result = await pool.query('SELECT * FROM tasks ORDER BY created_at DESC');
    res.json(result.rows);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.post('/tasks', async (req, res) => {
  const { title } = req.body;
  if (!title) return res.status(400).json({ error: 'Title is required' });
  try {
    const result = await pool.query(
      'INSERT INTO tasks (title) VALUES ($1) RETURNING *',
      [title]
    );
    res.status(201).json(result.rows[0]);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.patch('/tasks/:id', async (req, res) => {
  const { id } = req.params;
  const { completed } = req.body;
  try {
    const result = await pool.query(
      'UPDATE tasks SET completed = $1 WHERE id = $2 RETURNING *',
      [completed, id]
    );
    if (result.rows.length === 0) return res.status(404).json({ error: 'Task not found' });
    res.json(result.rows[0]);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Open package.json and update the "scripts" section:

"scripts": {
  "start": "node src/index.js",
  "dev": "nodemon src/index.js"
}

npm start runs the app directly with Node. npm run dev uses nodemon so the server restarts automatically when you edit a file.

For running without Docker, create a .env file:

DB_HOST=localhost
DB_PORT=5432
DB_NAME=tasksdb
DB_USER=postgres
DB_PASSWORD=yourpassword
PORT=3000

Notice that all database credentials come from environment variables rather than being hardcoded. Swap the variables, and the same image runs against your local database or a production one — no code changes needed. The /health endpoint is what Docker pings to know the app is actually handling requests.

Writing the Dockerfile

Before touching the Dockerfile, there are two terms you'll keep seeing. An image is a packaged, immutable version of your app — Node runtime, code, dependencies, everything together in one artifact. A container is a running instance of that image. One image, many containers, any machine.

Here's the Dockerfile we'll use:

# ── Stage 1: Install dependencies ──────────────────────────────────────────
FROM node:18-alpine AS builder

WORKDIR /app

# Copy package files first — Docker caches this layer separately.
# If you only change src code (not package.json), Docker skips npm ci on rebuild.
COPY package*.json ./
RUN npm ci

COPY . .


# ── Stage 2: Production image ───────────────────────────────────────────────
FROM node:18-alpine AS production

# Create a non-root user — running as root inside a container is a security risk
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodeuser -u 1001

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

# Copy only the source code from the builder stage (not node_modules or dev files)
COPY --from=builder /app/src ./src

RUN chown -R nodeuser:nodejs /app
USER nodeuser

EXPOSE 3000

# Docker will ping /health every 30s. If it fails 3 times, the container is marked unhealthy.
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "src/index.js"]

This is a multi-stage build. The first stage (builder) installs everything, including dev dependencies. The second stage (production) starts fresh and only copies what the app needs to run. Nodemon, test frameworks, and anything else dev-only never make it into the final image.

The size difference is real. A node:18 Debian image is over 950MB. Switch to node:18-alpine and cut out the dev dependencies, and the final image lands around 150–200MB instead. A smaller image means faster pushes and faster deploys.

npm ci instead of npm install is a deliberate choice for CI/CD. It reads exact versions from package-lock.json and fails hard if the lockfile doesn't match package.json. Every build on every machine installs the exact same versions — no surprises from a dependency that quietly updated overnight.

The nodeuser account exists because containers run as root by default. That's fine until something goes wrong. A non-root user means that an attacker who gets inside the container can't just do whatever they want.

The .dockerignore File

Create .dockerignore before building:

node_modules
npm-debug.log
.env
.git
.gitignore
README.md
Dockerfile
.dockerignore

The node_modules exclusion is the critical one. Your local modules were compiled for your operating system — macOS or Windows binaries won't work inside a Linux container. Excluding them means Docker installs fresh modules during the build, compiled for the correct platform. Without this exclusion, you'd either copy broken binaries into the image or waste time uploading hundreds of megabytes to the build context.

Never put .env in an image. Passwords, API keys, anything sensitive — those go in at runtime as environment variables, never inside the image itself.

The .gitignore File

One more thing before the first commit: a .gitignore. You don't want node_modules or .env tracked:

node_modules/
.env
.env.local
npm-debug.log*
logs/
.DS_Store
Thumbs.db
.vscode/
.idea/
dist/
build/

Build and Test the Image Locally

Open Docker Desktop first and give it a moment. On Windows, you'll see a whale icon in the taskbar that animates while the engine is starting up. Once it goes still, you're good to run Docker commands. If you try to run Docker before the engine is up, you'll hit this:

ERROR: Error response from daemon: Docker Desktop is unable to start

If that happens, quit Docker Desktop. Open PowerShell as Administrator, run wsl --update, and restart. Then go to Control Panel → Programs → Turn Windows features on or off. Both Hyper-V and Virtual Machine Platform need to be checked. After the restart, Docker Desktop should come up fine.

It's worth knowing about this error too:

docker : The term 'docker' is not recognized as the name of a cmdlet, function,
script file, or operable program.

This means that Docker Desktop isn't running or isn't installed. Open it from the Start menu and wait.

Run the build:

docker build -t nodejs-docker-cicd:latest .

The first time takes roughly 30 seconds since Docker has to pull node:18-alpine from the internet. Once that's cached, subsequent builds are much quicker. Both stages will scroll by:

[+] Building 33.1s (17/17) FINISHED
 => [builder 1/5] FROM docker.io/library/node:18-alpine       20.9s
 => [builder 4/5] RUN npm ci                                   3.5s
 => [production 5/7] RUN npm ci --only=production              3.2s
 => [production 7/7] RUN chown -R nodeuser:nodejs /app         3.2s
 => exporting to image                                         1.5s
 => => naming to docker.io/library/nodejs-docker-cicd:latest     0.0s

When you see (17/17) FINISHED the image is built. Check the size:

docker images nodejs-docker-cicd
IMAGE                     ID             DISK USAGE   CONTENT SIZE
nodejs-docker-cicd:latest   c9eed311d999        198MB         47.5MB

CONTENT SIZE (47.5MB) is the compressed size that gets pushed to Docker Hub. DISK USAGE (198MB) is what it takes up on disk locally. Compare that to a node:18 Debian image at 950MB+, and you can see why the Alpine base and multi-stage approach matter.

On subsequent builds, Docker reuses cached layers. Edit only your source files without touching package.json and the npm ci step gets skipped completely. That 33-second first build becomes 3 seconds.

Docker Compose for Local Development

The app needs a database. Setting up PostgreSQL locally means every developer who clones the repo has to do it, too. Docker Compose handles this: one file defines both services, and one command starts them.

Create docker-compose.yml:

services:
  app:
    build:
      context: .
      target: production
    ports:
      - '3000:3000'
    environment:
      DB_HOST: postgres
      DB_PORT: 5432
      DB_NAME: tasksdb
      DB_USER: postgres
      DB_PASSWORD: postgres
      PORT: 3000
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped

  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: tasksdb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - '5432:5432'
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U postgres']
      interval: 5s
      timeout: 5s
      retries: 5

volumes:
  postgres_data:

A few things worth pointing out. DB_HOST is set to postgres. That's the service name, not localhost. Containers on the same Docker network reach each other by service name. Put localhost there and the app tries to connect to itself.

depends_on with condition: service_healthy holds the app back until Postgres actually passes its health check. Skip this and the app starts, tries to connect to a database that isn't ready yet, and crashes. The health check pings pg_isready every 5 seconds. Once it gets a green response, the app container starts.

The named volume postgres_data keeps your data alive between restarts. Run docker compose down and the data is still there next time. Add --volumes to wipe it clean.

Start both services:

docker compose up --build

You'll see PostgreSQL initialize and then the app start. Once you see Server running on port 3000 in the logs, the stack is up.

Open a second terminal to test — leave the compose logs running in the first one.

Linux/macOS:

curl -X POST http://localhost:3000/tasks \
  -H "Content-Type: application/json" \
  -d '{"title": "Learn Docker"}'

curl http://localhost:3000/tasks

curl http://localhost:3000/health

Windows PowerShell: Typing curl in PowerShell runs Invoke-WebRequest, not actual curl. Run curl.exe instead. For JSON bodies, write to a file first:

'{"title": "Learn Docker"}' | Set-Content body.json
curl.exe -X POST http://localhost:3000/tasks -H "Content-Type: application/json" --data `@body.json

curl.exe http://localhost:3000/tasks

curl.exe http://localhost:3000/health

The backtick before @body.json is necessary. PowerShell would otherwise try to interpret @ as a splatting operator rather than passing it to curl as a filename prefix.

You should see responses like these:

# POST /tasks
{"id":1,"title":"Learn Docker","completed":false,"created_at":"2026-07-09T22:21:17.073Z"}

# GET /tasks
[{"id":1,"title":"Learn Docker","completed":false,"created_at":"2026-07-09T22:21:17.073Z"}]

# GET /health
{"status":"ok","timestamp":"2026-07-09T22:11:44.700Z"}

The task hit PostgreSQL in one container and came back through the app. Ctrl+C in the compose terminal stops both.

Automate the Build with GitHub Actions

The image works locally, so it's time to stop doing this by hand.

Step 1: Create a Docker Hub Access Token

Go to hub.docker.com and then Account Settings → Security → New Access Token. Set permission to Read & Write, as read-only breaks the push. The token appears once, so copy it before closing the page.

Security warning: Don't paste this token into a chat, email, or commit. If you expose it by accident, delete it immediately, then make a new one.

Step 2: Add Secrets to Your GitHub Repository

Head to Settings → Secrets and variables → Actions in your repo and add:

  • DOCKERHUB_USERNAME — your Docker Hub username

  • DOCKERHUB_TOKEN — paste the token here, nowhere else

If you ran into Error: Username and password required, the secrets either aren't saved yet or the names are typed wrong. Both are case-sensitive.

A Node 20 deprecation warning in the logs is normal. It comes from the Docker actions internally, not your code.

Step 3: Create the Workflow File

Create .github/workflows/docker-publish.yml:

name: Build and Push Docker Image

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  IMAGE_NAME: ${{ secrets.DOCKERHUB_USERNAME }}/nodejs-docker-cicd

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix=sha-
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          target: production
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

The login step has if: github.event_name != 'pull_request'. This skips authentication on pull requests. PRs from forks don't have access to your secrets, so trying to log in would just fail. The build still runs on PRs to validate your Dockerfile, but the image isn't pushed.

The metadata action generates two tags on every merge to main: latest and a short commit SHA like sha-a1b2c3d. The SHA tag is what makes rollbacks practical. If latest breaks in production, you can pull any previous sha- tag and you're back to a known-good state in seconds.

The cache-from/cache-to: type=gha lines store Docker's layer cache in GitHub Actions' built-in cache. The first run builds everything from scratch. After that, unchanged layers are pulled from cache rather than rebuilt. On a typical Node.js app this brings build time from 2–3 minutes down to under 30 seconds.

Push and Watch it Run

git add .
git commit -m "Add Docker configuration and GitHub Actions workflow"
git push origin main

Go to your repo's Actions tab. You'll see the workflow running in real time. Each step turns green as it completes:

✅ Checkout code
✅ Set up Docker Buildx
✅ Log in to Docker Hub
✅ Extract metadata
✅ Build and push

Green across the board means your image is live on Docker Hub — two tags, latest and a commit SHA like sha-a1b2c3d. Every push to main from here builds and ships automatically.

Deploying the Image

With your image on Docker Hub, you can deploy it to any infrastructure:

Any VPS or server:

docker pull yourusername/nodejs-docker-cicd:latest
docker run -d -p 3000:3000 \
  -e DB_HOST=your-db-host \
  -e DB_NAME=tasksdb \
  -e DB_USER=postgres \
  -e DB_PASSWORD=yourpassword \
  yourusername/nodejs-docker-cicd:latest

Railway — Connect your Docker Hub image in the Railway dashboard and it deploys on the next push.

Fly.io — Run fly launch pointing at your Dockerfile and Fly handles the rest.

Render — Paste your Docker Hub image URL into the Render service settings.

Each push to main runs the workflow. New image goes to Docker Hub, platform picks it up — that's your deployment handled.

Wrapping Up

What started as a local Node.js app now runs in a container. You get the same behavior on any machine, real PostgreSQL in development, and a pipeline that builds and ships to Docker Hub without you doing anything after the push.

The multi-stage build keeps the image lean — dev tools stay out, non-root user, health check baked in. Compose gets the full stack up with one command for anyone who clones the repo. The SHA tag on every GitHub Actions build means rolling back is just a matter of pulling an older tag.

These same patterns (multi-stage builds, Compose for local development, automated image publishing) are used across the industry for production Node.js deployments. Pick up these patterns once and they follow you to every project.

From here, you can extend the pipeline: drop a test step in before the build, or add multi-platform support if you're targeting ARM. Once Docker Compose starts feeling limiting in production, that's usually when Kubernetes enters the picture.



Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Rethinking C++ Performance: Faster Code Navigation and GitHub Copilot Tools with Whole Codebase Indexing

1 Share

In large C++ codebases, your code understanding and navigation depend on quickly determining how symbols, declarations, definitions, and references are connected across your project.

In Visual Studio Insiders 18.9, the new whole codebase indexing (WCI) enhances the existing browse database via a deeper, more comprehensive indexing approach. This preview feature allows Visual Studio to access richer symbol information more efficiently in your C++ project, with faster lookups for core IntelliSense scenarios like Find All References and semantic file colorization. It also enables new experiences like CodeLens references for C++ , a highly requested productivity feature. The same symbol index supports the find_symbol tool, which gives Copilot faster access to symbol context, providing more responsive agentic suggestions.

In this blog, we discuss:

  • Faster file colorization and code navigation with WCI
  • Faster access to symbol-level context for GitHub Copilot tools
  • CodeLens for C++
  • Technical implementation details

Faster File Colorization and Code Navigation

Building code understanding features for C++ is uniquely challenging due to the language’s complexity, especially at scale in modern codebases. Whole codebase indexing (WCI) adds a deeper indexing approach designed for faster, more accurate C++ code navigation. For a deeper technical explanation, see the Technical Details section.

Prior to WCI, many code navigation features relied on a combination of information from the browse database and on-demand analysis of translation units to resolve C++ symbols.  Because this information often had to be computed at request time, navigation operations could take longer to complete and require additional time and resources. This was especially noticeable for repeated operations, as the same symbol information had to be recomputed every time. Now, deeper symbol information is stored in the database and continuously updated as you code, leading to faster, more efficient lookups.

While results vary between codebases and operations, we consistently found 2x or greater improvements for many code navigation and semantic colorization scenarios with WCI enabled.  These accumulate as repeated operations can continue using the same database, with the largest speedups often occurring in larger projects. In some scenarios, operations that used to take seconds to wait for, like file colorization, are now nearly instantaneous, creating a significantly more responsive editing experience.

With WCI enabled, Visual Studio enhances the C++ browse database in the background with richer symbol information. This information is indexed on demand on file open and persists between Visual Studio sessions, with the database expanding as you open new files and work across your codebase.  If the required data is not yet (or only partially) available, Visual Studio automatically falls back to the existing implementation. When this happens, all code navigation features continue to work, but without the performance gains from WCI.

The examples below show these improvements on two open-source codebases of different sizes: the smaller Bullet3 repository and the larger LLVM project. All testing was done with Visual Studio 2026 version 18.9.

In the two examples below, building the initial on-demand index required for these scenarios took approximately 22 seconds for Bullet3 (91 translation units indexed across 2 files) and approximately took 3.5 minutes for LLVM (136 translation units indexed across 5 files in the LLVM core project). These measurements are examples, actual indexing time and resources depends on the size and complexity of your codebase as well as the number of files opened at once.

The charts below show the average completion time for semantic file colorization and Find All References calls across both codebases with and without WCI enabled.

Graph, 414ms with bullet3 down to 9ms. 1156ms down to 19ms with llvm

graph, find all references (in seconds), 18s to 1s in bullet3, 134s to 69s L:LVM
Test environment: Microsoft Dev Box, AMD EPYC 7763 (8 physical cores / 16 logical processors @ 2.45 GHz), 64 GB RAM, running Windows 11 Enterprise. Results were collected across multiple benchmark runs and averaged for consistency.     

In many cases, like in LLVM above, the improvements are large enough that colorization no longer feels like a background operation. Instead, once the deeper semantic index has been created, the semantic colorization feels nearly instantaneous. For example, it only takes 0.2 seconds to colorize the PassBuilder.cpp file from Bullet3:

SidebySide WCI 3 1 sharp image
In the time it takes for the previous implementation (18.9) to colorize the file, 18.9 with WCI is able to colorize the file and shortly after also have CodeLens support available. Note that this entire gif is running at half speed (including the timer).

Faster access to symbol-level context for agentic suggestions

WCI also improves C++ agentic suggestions by giving Copilot faster access to rich, symbol-level context from your C++ codebase through the find_symbol tool. This tool is backed by language service protocol (LSP) symbol operations, and WCI’s expanded symbol index helps these operations locate relevant types, functions, declarations, and definitions across the codebase more quickly and directly.

copilot1 image

This deeper integration with Visual Studio’s C++ code intelligence helps Copilot spend less time searching for symbol information, making the full agentic loop quicker and more responsive. In larger codebases, especially when symbols have many reference locations, this helps C++ agentic suggestions execute faster and be more relevant than workflows that depend only on file search or language-agnostic code context.

copilot2 image

Faster Navigation via CodeLens for C++

WCI’s richer symbol information also enables new experiences in the editor. For example, enabling the “Enable CodeLens for References” sub-setting for WCI enables a highly requested capability: reference support for CodeLens in C++. Since this feature is currently in preview, it is off by default.

With WCI powering CodeLens, you can now see reference counts directly inline above your functions or symbols. These are available for any symbol indexed by WCI, with the full list of references accessible via a single click. Now, there’s no need to manually run “Find All References” or switch to a separate results window. Symbol usage, definitions, and declarations are also shown inline.

CodeLensforCpp image

Note, to use CodeLens for C++, your project also needs to have the CodeLens setting (Tools > Options > Text Editor > CodeLens) enabled.

Technical details

Traditionally, the browsing database tracks only declarations and definitions of symbols in a codebase, and is populated by the TagParser. The TagParser is optimized for speed, and does not expand includes or perform full name resolution. It is a different C++ parser than the one used in the IntelliSense engine, and that tradeoff for speed versus accuracy can result in ambiguities that need to be resolved during the operation by the IntelliSense engine.

In WCI, the database is expanded to track symbol usages as well, using the full capabilities of the IntelliSense engine. The result is more precise semantic information in the database, and the ability to use that data directly to serve the operations for semantic colorization and navigation across files, instead of having to wait for the IntelliSense engine to be initialized, which can have a higher latency cost.

The database now acts as a caching layer for the IntelliSense operations: it uses the database if a given file has been indexed, and if not, it falls back to the IntelliSense engine as before. Because that precise information might take more time and resources to compute, by default the files are progressively indexed on demand based on their usage. This allows the cost to be amortized over time, especially for large codebases. With a smaller codebase, consider enabling the sub setting “Parse all files in the solution ahead of time” to index all project files once on project open.

This model produces more predictable latency curve, with a lower average overall, but also fewer operations experiencing long tail latency, in cases where complex code constructs would take more time to process and delay the results, since that processing can now be non-blocking and reused more often.

A Few Things to Keep in Mind

  • Machines need to have a minimum of 4+ cores to use this process, which is the recommended minimum hardware requirement for Visual Studio 2026.
  • Building a symbol index with WCI for your project may take up additional processing and memory resources on your machine compared to using the previously.  To check the current status while indexing is in progress, look for a task notification in Visual Studio’s task manager called “Running deep C++ analysis for richer navigation”.
  • This is a preview feature that is gradually being rolled out to specific groups, so the setting might be already enabled on your machine.  If you want to enable it yourself, you can always navigate to the setting in the preview feature (Tools > Options  > Whole codebase semantic index for C++)

Try it today & tell us what you think

Try out this feature today in your own codebase by enabling the setting in the preview feature (Tools > Options  > Whole codebase semantic index for C++).

To check whether the setting has been enabled, navigate to Tools > Options > Languages > C/C++ > IntelliSense > Browsing & navigation > whole codebase semantic index > enable faster code navigation and colorization features (experimental).

This feature exists because of your feedback, and we will continue to improve. We would love to hear how this deeper indexing is working for you. Please share your thoughts by filling out this survey , commenting below, through Help > Send Feedback in Visual Studio, on Bluesky (@msftcpp.bsky.social) or on X (@VisualC). Thank you for your continued support!

To learn more: Configure IntelliSense Options for C and C++ – Visual Studio (Windows) | Microsoft Learn

The post Rethinking C++ Performance: Faster Code Navigation and GitHub Copilot Tools with Whole Codebase Indexing appeared first on C++ Team Blog.

Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing Usage Limits for Pulumi Neo

1 Share

Pulumi Neo is an AI agent that takes on real infrastructure work, and it’s natural to want to hand it more and more. Usage limits give you control so you can do exactly that: set a monthly dollar limit, and Neo pauses when your organization reaches it.

How usage limits work

Your organization limit is a single monthly dollar amount covering all Neo usage across the org. To set one:

  1. In the Pulumi Cloud console, navigate to Settings → Billing & usage → Neo token usage.
  2. In the Manage token usage panel, enter an organization limit.
  3. Save your changes.

When usage reaches the limit, Neo pauses for the rest of the billing period and resumes automatically at the start of the next one. An Admin or Billing Manager can raise the limit to resume before then.

The Manage token usage panel, where an admin sets the organization’s monthly Neo limit and turns on email notifications.

Enforcement happens at a natural boundary in Neo’s work, so a task already in progress finishes its current step before pausing. As a result, usage can go a few dollars over the set limit.

Per-member limits and alerts

You can also set a separate limit for each member. A member is paused at whichever limit is smaller: their own or the organization’s. For example, a member with a $200 limit under a $150 organization limit pauses at $150, because the organization limit is smaller.

The per-member limits table, showing each member’s amount used and effective limit for the billing period.

Turn on Enable email notifications to get a heads-up before you reach the limit. Billing admins are alerted at 50%, 80%, and 95% of the organization limit, with a final notice at 100% when Neo pauses.

Get started

Set your usage limits and stay in control as your organization hands Neo more and more work. Usage limits are available today for organizations on a paid plan, and an Admin or Billing Manager can set them.

Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

ASP.NET Core updates in .NET 11 Preview 6

1 Share

Here's a summary of what's new in ASP.NET Core in this preview release:

ASP.NET Core updates in .NET 11:

Read the whole story
alvinashcraft
37 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

OpenAI may announce a ChatGPT smart speaker this year

1 Share

OpenAI's first device is set to be a smart speaker that lets you talk with ChatGPT, according to a report from Bloomberg. The device apparently won't have a screen, but will use a camera and additional sensors to "understand" your environment.

The report comes just days after Apple filed a lawsuit against OpenAI that accused the AI company of stealing hardware secrets. OpenAI, in a new statement on Tuesday, said that it is "not aware of any evidence that this complaint has merit."

Sources tell Bloomberg that OpenAI's device will also feature a rechargeable battery that will allow users to carry it with them. It will offer smart home contro …

Read the full story at The Verge.

Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete

OpenAI hits 8 million Codex users — what developers need to know

1 Share
Sam Altman, OpenAI CEO

OpenAI launched GPT-5.6 last week and folded Codex into a unified ChatGPT desktop app. Since then, the company has been running flat out.

Early Tuesday, Tibo Sottiaux, engineering lead for Codex at OpenAI, mused on X that combined active users of Codex and ChatGPT Work might hit 8 million. The trajectory has been impressive, especially as the use of Anthropic Claude has grown so much in 2026. Codex had fewer than 1 million weekly active users in February but hit 5 million by early June. Then GPT-5.6 launched on July 9, and the numbers accelerated sharply — 6 million by July 12, 7 million roughly 24 hours later, and 8 million by Sunday.

That’s a growth curve most enterprise SaaS products never see in a lifetime, let alone in five months.

What broke at launch

OpenAI merged the standalone Codex app into the ChatGPT desktop app, launched ChatGPT Work as a new agentic mode for knowledge workers, and began sunsetting the Atlas browser — all in a single day.

According to the company, demand surged almost immediately, with traffic roughly doubling OpenAI’s previous peak within 48 hours. The sudden influx exposed several scaling issues. In a detailed thread published on July 12, Sottiaux outlined the team’s response, which included optimizing inference to increase capacity by about 10% per subscriber, reducing the context window from 372,000 to 272,000 tokens after the larger limit created unintended billing issues, rolling back experimental reasoning-effort settings (internally known as “juice” values), and patching overly aggressive multi-agent behavior at the highest reasoning levels.

OpenAI also temporarily removed the five-hour usage cap for Plus, Business, and Pro subscribers — a move that amounts to the most generous access the product has offered since launch.

Community reactions were split between those who read the context window rollback as a stealth downgrade and those who credited Sottiaux for explaining the operational trade-offs publicly. OpenAI CEO Sam Altman weighed in with what amounted to a positioning statement against competitors who, in his framing, treat users with contempt.

Competitors respond to the surge

Within hours of OpenAI announcing its 7 million user milestone, Anthropic extended its Claude Fable 5 promotional pricing through July 19 and bumped Claude Code’s weekly usage limits by 50%. Whether one caused the other is unknowable, but the overlap gives us plenty to speculate about.

Since Thursday, GPT-5.6 Sol has risen to second on Arena’s agent leaderboard after 7,800 real-world agentic sessions, and many developers now see OpenAI as the leader in AI coding again.

Cost per task matters

Developers judge systems by the cost of completing a task. Cognition, for example, reported that its Devin Fusion product, powered by Fable 5, can be cheaper per completed task than Anthropic’s pricier Opus 4.8 because better delegation reduces unnecessary work. In 81% of Fable-led runs, the lead model never edits code. The takeaway is that a more expensive model can still lower overall costs if it avoids wasted work.

In 81% of Fable-led runs, the lead model never edits code.

The harness is becoming the product

OpenAI’s decision to merge Codex, ChatGPT Work, and its built-in browser into a single desktop app reflects that shift. The same is true of its plugin architecture, which connects the app to Slack, Google Drive, SharePoint, CRMs, and calendars. OpenAI is turning it into a workspace that sits atop the tools people already use.

Anthropic has moved in a similar direction with Claude Cowork, which developed after the company saw users already using Claude Code for far more than software development.

Usage caps remain the bottleneck

Right now, the problem remains the usage cap. Even with the five-hour limit lifted, Codex and ChatGPT Work are forced to share a single weekly pool. If you run a heavy Sol Ultra session with multi-agent orchestration, you’ll burn through that allowance incredibly fast. (Sottiaux is aware of the headache and says a fix is in the works).

In the broader race, OpenAI has the sheer gravity of 900 million weekly users and killer benchmark scores. Anthropic still holds a massive amount of developer goodwill thanks to premium code quality and enterprise-friendly pricing. And for the DIY crowd, open-source stacks running GLM 5.2 or Kimi K2.7 look better than ever if you want to trade a little speed for data control. Coding agents and knowledge work are multiplying fast, and we haven’t even hit the weekend yet.

Coding agents for coding and knowledge work are multiplying fast, and we haven’t even hit the weekend yet.

The post OpenAI hits 8 million Codex users — what developers need to know appeared first on The New Stack.

Read the whole story
alvinashcraft
2 hours ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories