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

Don’t skip today’s Windows 11 update. Microsoft just patched a record 570 flaws, 4x last year as AI accelerates attacks

1 Share

Microsoft rolled out a massive July 2026 Patch Tuesday update, patching a record-breaking 570 security flaws. If you’ve been following Windows development, you’d realize how big of a deal it is, but for those who are unaware, that’s a 316% increase from the 137 vulnerabilities fixed in July 2025 and a 185% jump from the 200 flaws patched in June 2026.

In fact, Windows 11’s July 2026 Patch Tuesday update is more than four times as large as the July 2025 release and nearly three times as large as last month’s update. I made a table that explains how massive the situation is, and as you can see, Patch Tuesday updates are more important than ever:

Month Flaws fixed in 2025 Flaws fixed in 2026 Year-over-year change
January 159 114 -28.3%
February 55 58 +5.5%
March 57 79 +38.6%
April 134 167 +24.6%
May 72 120 +66.7%
June 66 200 +203%
July 137 570 +316.1%
January–July total 680 1,308 +92.4%

Does that mean Windows is now more vulnerable, or has Microsoft become more capable of identifying bugs? Well, the correct answer is neither, and it’s deeply tied to how AI is changing cybersecurity.

How to verify if your Windows 11 PC received the 570 security fixes

With the July 2026 update, Microsoft has bumped Windows 11 25H2 to Build 26200.8875 (KB5101650). If you are still on Windows 11 24H2, you’ll be getting Build 26100.8875, but nothing is really different.

You can verify that the update is installed by checking whether your PC is running Build 26200.8875 on Windows 11 25H2 or Build 26100.8875 on Windows 11 24H2.

2026-07 Security Update (KB5101650) (26200.8875)
July 2026 update for Windows 11

KB5101650 also adds multiple new features, including Point-in-time restore, faster Bluetooth for AirPods, and a more reliable File Explorer.

How big is the July 2026 update for Windows 11?

Patch Tuesday includes security fixes for all Microsoft products, but most of the changes are for Windows. For example, Microsoft fixed security issues in the kernel, Win32k, NTFS, Remote Desktop, DHCP, TCP/IP, Hyper-V, Secure Boot, BitLocker, File Explorer, Print Spooler, Media Foundation, USB drivers, SMB, and Windows Installer.

That’s nearly every core component of Windows under the sights of potential bad actors, which is quite rare and nasty.

Microsoft is also patching several critical flaws that could allow attackers to remotely execute code through networking, media, graphics, storage, or server components.

Microsoft officials recently confirmed that the number of addressed vulnerabilities is set to increase in the coming months, and that the risk is real, thanks to AI.

Do not pause Windows updates unless absolutely necessary

You shouldn’t pause Windows updates unless your PCs are taking a major hit due to these monthly updates.

Calendar to pause updates in Windows 11

The Windows security situation is only going to get worse from here, and quite interestingly, Microsoft warned against pausing updates days before the massive July 2026 security update.

“There’s been a significant increase in security updates across the industry,” warns Jeremy, who is a director at Microsoft 365.

Microsoft found that bad actors are using AI to find bugs in Windows and actively exploit unpatched systems.

AI models for security

To combat this, Microsoft is using its own AI system called MDASH to identify bugs in Windows and patch them before attackers find their way through. As a result, you’re going to see larger Patch Tuesday updates going forward.

MDASH is a multi-model agentic scanner that identifies bugs, while agents debate among themselves to find the root causes.

MDASH

The company is not using any specific AI model here, as it’s a mix of different models, including powerful models made by Anthropic.

With MDASH, Microsoft found 16 major security issues in the Windows networking and authentication stack. If you think that’s bad, the total number of critical remote code execution flaws stood at four in MDASH’s initial scan.

It’s not just Windows, Microsoft says in defense of the growing number of bugs

Microsoft argues that Windows security issues aren’t an isolated case, as it’s an industry-wide problem.

“This is on par with the rest of the industry, with AI speeding up how quickly software vulnerabilities are found and exploited, often from weeks to hours, including zero days,” the company said in a statement.

As Windows Latest previously reported, Microsoft has warned against deferring updates beyond three days. The warning came directly from a Microsoft 365 director, and you can watch it below:

Microsoft is also updating its guidelines to encourage installing Windows updates as soon as they’re available.

“To address this, we’ve updated our recommendations for deploying Windows updates to less than three days as the deferral period for quality updates, setting deadlines for those updates to zero or one day, and the update grace period to a maximum of two days,” Microsoft explained.

Microsoft is advising IT admins to start using policy controls to ensure updates are deployed as soon as they’re available and consider moving to Windows Autopatch and Microsoft Intune.

It doesn’t matter if you are an IT admin or a regular consumer. The threat of AI is real, and Windows has never been the most secure operating system, contrary to what Microsoft claims. If anything, you should install these monthly updates and consider pausing Windows updates only in the worst-case scenario.

The post Don’t skip today’s Windows 11 update. Microsoft just patched a record 570 flaws, 4x last year as AI accelerates attacks appeared first on Windows Latest

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

MBW 1033: Liquid Glass Half Full - Apple Sues OpenAI Over Company Secrets

1 Share

Apple sues OpenAI! Public beta for iOS 27 & more is now available. Apple seeks to move to the M7 chips quickly. And Apple TV gets a record 87 total Emmy nominations!

  • Apple sues OpenAI, accusing it of stealing company secrets.
  • Apple says former employee exploited 'rare' bug to download confidential files after leaving for OpenAI.
  • Apple's public betas for iOS 27 and more are out now.
  • First Look: iOS 27 & macOS Golden Gate public beta.
  • watchOS 27 public beta is here with Siri AI and smarter Apple Watch features.
  • AirPods firmware beta lets developers use new iOS 27 features.
  • M6 era will last just six months as Apple pushes for AI-focused M7.
  • iPhone 18 Pro Max component costs could jump by nearly $300, per report.
  • No delay to iPhone Ultra, says supply-chain report.
  • Lamborghini App for Vision Pro.
  • Component development for cheaper Apple Vision Pro reportedly scrapped.
  • Emmy Awards 2026: Apple TV just landed a record 87 nominations.
  • Apple TV sets major Comic-Con lineup with Silo, Dark Matter, Widow's Bay, more.
  • Apple TV has a packed lineup of sports premieres coming soon.
  • TV-logging app TV Time is functionally relaunching as parent company pivots to AI.

Picks of the Week

  • Jason's Pick: Television Time
  • Andy's Pick: Colorful Life
  • Christina's Pick: GitHub Copilot Desktop A

Hosts: Leo Laporte, Andy Ihnatko, Jason Snell, and Christina Warren

Download or subscribe to MacBreak Weekly at https://twit.tv/shows/macbreak-weekly.

Join Club TWiT for Ad-Free Podcasts!
Support what you love and get ad-free audio and video feeds, a members-only Discord, and exclusive content. Join today: https://twit.tv/clubtwit

Sponsors:





Download audio: https://pdst.fm/e/pscrb.fm/rss/p/mgln.ai/e/294/cdn.twit.tv/megaphone/mbw_1033/ARML3899801088.mp3
Read the whole story
alvinashcraft
3 hours ago
reply
Pennsylvania, USA
Share this story
Delete

Building a Custom Metrics Exporter for Kubernetes

1 Share

Kubernetes ships with built-in awareness of CPU and memory, but most real-world scaling decisions depend on signals that live entirely outside that narrow window: how many messages are waiting in a queue, how long the last batch job took, how many active WebSocket connections a pod is holding. When the built-in metrics are not enough, a metrics exporter bridges that gap.

This post walks through writing one from scratch, packaging it as a container, and wiring it into a cluster so that Prometheus — and ultimately the HorizontalPodAutoscaler — can consume it.

What a metrics exporter actually does

An exporter is a small HTTP server with a single responsibility: expose application state as text on a /metrics endpoint. Prometheus scrapes that endpoint on a regular interval, stores the time-series data, and makes it available for queries, alerts, and autoscaling rules.

In some cases you can instrument your application directly — embedding the Prometheus client library and exposing /metrics from within the same process — rather than running a separate exporter. A standalone exporter makes more sense when the data source is external to your application or when you do not control the application code.

The format Prometheus expects is plain text — one metric per line, with a name, optional labels, and a numeric value. Client libraries handle the serialization for you, so in practice you only need to decide what to measure and call the right function when that value changes.

Choosing what to measure

Before writing any code, it helps to decide what kind of signal you are dealing with. The Prometheus data model has three main types:

  • Counters only ever increase. They are the right tool for totals: requests served, jobs processed, errors encountered. Never use a counter for a value that can go down.

  • Gauges represent a current snapshot of a value that can rise and fall freely. Queue depth, active connections, and cache size are all gauges.

  • Histograms record the distribution of observed values, such as request latency. They let you calculate percentiles (p99, p50) rather than just averages.

Once you know which type fits your signal, choose a name that follows the convention <namespace>_<name>_<unit> in snake_case. A job processor might expose worker_jobs_processed_total (counter), worker_queue_depth (gauge), and worker_job_duration_seconds (histogram). Clear names save everyone debugging time later.

Setting up the project

The Go Prometheus client is the most common choice for exporters in the Kubernetes ecosystem, largely because the same library powers most of the official Kubernetes components. Start by creating a module and pulling in the dependency:

mkdir my-exporter && cd my-exporter
go mod init example.com/my-exporter
go get github.com/prometheus/client_golang/prometheus
go get github.com/prometheus/client_golang/prometheus/promhttp

Registering metrics

Create main.go. The first thing to do is declare the metrics and register them with Prometheus's default registry. Registration tells the library that these metrics exist so they appear in the output even before the first observation is recorded:

package main

import (
 "log"
 "net/http"

 "github.com/prometheus/client_golang/prometheus"
 "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
 jobsProcessed = prometheus.NewCounterVec(
 prometheus.CounterOpts{
 Name: "worker_jobs_processed_total",
 Help: "Total number of jobs processed, partitioned by status.",
 },
 []string{"status"},
 )

 queueDepth = prometheus.NewGauge(prometheus.GaugeOpts{
 Name: "worker_queue_depth",
 Help: "Current number of jobs waiting in the queue.",
 })

 jobDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
 Name: "worker_job_duration_seconds",
 Help: "Time spent processing a single job.",
 Buckets: prometheus.DefBuckets,
 })
)

func init() {
 prometheus.MustRegister(jobsProcessed, queueDepth, jobDuration)
}

prometheus.MustRegister panics on a duplicate registration, which makes misconfigurations obvious at startup rather than silently at runtime. If you are embedding this exporter inside a library that other packages will also instrument, prefer prometheus.Register and handle the error yourself.

Collecting real values

With the metrics registered, the next step is to keep them current. You can either continually update the data as the data change, or run your own internal refresh loop. The pattern below shows a polling loop — a goroutine that periodically reads from whatever data source your application owns and updates the registered metrics. Replace the simulated values with real calls to your database, internal API, or message broker:

import (
 "math/rand"
 "time"
)

func collectMetrics() {
 for {
 // Replace these with real reads from your application.
 depth := float64(rand.Intn(50))
 queueDepth.Set(depth)

 start := time.Now()
 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)
 jobDuration.Observe(time.Since(start).Seconds())
 jobsProcessed.WithLabelValues("success").Inc()

 time.Sleep(5 * time.Second)
 }
}

The polling interval (here five seconds) should be shorter than Prometheus's scrape interval so that each scrape sees a fresh value. The default scrape interval in most cluster deployments is fifteen seconds, which gives you comfortable headroom.

Exposing the endpoint

Wire the collection loop and the HTTP handler together in main. A /healthz path alongside /metrics gives Kubernetes a liveness probe target without exposing metric data on the health route:

func main() {
 go collectMetrics()

 http.Handle("/metrics", promhttp.Handler())
 http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
 w.WriteHeader(http.StatusOK)
 })

 log.Println("Listening on :8080")
 if err := http.ListenAndServe(":8080", nil); err != nil {
 log.Fatalf("server error: %v", err)
 }
}

Verify the output locally before building the image:

go run .
curl http://localhost:8080/metrics | grep worker_

You should see three # HELP and # TYPE blocks followed by the current metric values. If those lines appear, the exporter is working correctly and is ready to be containerized.

Build a container image

A multi-stage build keeps the final image small and avoids shipping a Go toolchain to production. The first stage compiles a statically linked binary; the second stage copies only that binary into a minimal base. The example below uses Docker, but the same pattern works with any OCI-compatible build tool such as Buildah or Podman:

FROM golang:1.21-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /exporter .

FROM gcr.io/distroless/static:nonroot
COPY --from=builder /exporter /exporter
EXPOSE 8080
ENTRYPOINT ["/exporter"]

distroless/static:nonroot contains no shell, no package manager, and runs as a non-root user by default, which satisfies most cluster security policies without extra configuration.

Build and push the image, replacing <registry> with your own registry address:

docker build -t <registry>/my-exporter:v1.0.0 .
docker push <registry>/my-exporter:v1.0.0

(Note: Using a CI/CD pipeline to automate this is generally a better pattern than running these commands manually.)

Deploying to the cluster

Two manifests are enough to run the exporter: a Deployment that manages the pod lifecycle, and a Service that gives Prometheus a stable address to scrape. (You might prefer to have Prometheus scrape from every Pod; if that makes sense for your use case, then it's OK to configure instead).

The examples below use the monitoring namespace, which is a common convention when running Prometheus and related components together. Adjust the namespace to match your own cluster setup.

The Deployment sets conservative resource limits appropriate for a lightweight sidecar-style process, and uses the /healthz route for its liveness probe:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: my-exporter
 namespace: monitoring
 labels:
 app.kubernetes.io/name: my-exporter
spec:
 replicas: 1
 selector:
 matchLabels:
 app.kubernetes.io/name: my-exporter
 template:
 metadata:
 labels:
 app.kubernetes.io/name: my-exporter
 spec:
 containers:
 - name: exporter
 image: <registry>/my-exporter:v1.0.0
 ports:
 - name: metrics
 containerPort: 8080
 livenessProbe:
 httpGet:
 path: /healthz
 port: 8080
 initialDelaySeconds: 5
 periodSeconds: 10
 resources:
 requests:
 cpu: 50m
 memory: 32Mi
 limits:
 cpu: 100m
 memory: 64Mi

The Service names the port metrics, which the ServiceMonitor in the next section will reference by that name:

apiVersion: v1
kind: Service
metadata:
 name: my-exporter
 namespace: monitoring
 labels:
 app.kubernetes.io/name: my-exporter
spec:
 selector:
 app.kubernetes.io/name: my-exporter
 ports:
 - name: metrics
 port: 8080
 targetPort: metrics

Apply both:

kubectl apply -f deployment.yaml -f service.yaml

Telling Prometheus where to look

How you configure scraping depends on how Prometheus was installed.

Option 1: Prometheus Operator (ServiceMonitor)

If you installed Prometheus using the Prometheus Operator or the kube-prometheus-stack Helm chart, the operator must be running in your cluster before you create a ServiceMonitor. The release label must match the label selector configured on your Prometheus resource — kube-prometheus-stack is the default for a standard Helm install:

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
 name: my-exporter
 namespace: monitoring
 labels:
 release: kube-prometheus-stack
spec:
 selector:
 matchLabels:
 app.kubernetes.io/name: my-exporter
 endpoints:
 - port: metrics
 interval: 15s
 path: /metrics

Option 2: Annotation-based discovery

If your Prometheus uses annotation-based pod discovery instead, you will need a matching scrape_config rule in your Prometheus configuration — check with whoever manages your Prometheus installation to confirm it is in place.

You can add the following two annotations to the Pod template regardless of which scraping method you use. They are ignored by the Prometheus Operator but picked up automatically by annotation-based setups:

annotations:
 prometheus.io/scrape: "true"
 prometheus.io/port: "8080" # omit if not using annotation-based discovery
 prometheus.io/path: "/metrics" # omit if not using annotation-based discovery

If you are unsure which setup your cluster uses, the ServiceMonitor approach is more explicit and easier to debug.

Verifying the scrape

Port-forward to the Prometheus service and open the targets page to confirm the exporter has been discovered:

kubectl port-forward svc/prometheus-operated 9090 -n monitoring

Navigate to http://localhost:9090/targets. The my-exporter target should appear with state UP. If it shows DOWN, check that the ServiceMonitor's release label matches and that the pod is running:

kubectl get pods -n monitoring -l app.kubernetes.io/name=my-exporter
kubectl describe servicemonitor my-exporter -n monitoring

Once the target is healthy, run a quick query in the expression browser to confirm data is flowing:

rate(worker_jobs_processed_total{status="success"}[2m])

A non-zero result here means the full pipeline is working: your application is producing data, Prometheus is scraping it, and the time-series are stored and queryable.

What comes next

A working exporter is the foundation, not the destination. The natural next step is surfacing these metrics to the HorizontalPodAutoscaler so that your workload scales on the signals that actually drive load, not just CPU. That requires a metrics adapter — the Prometheus Adapter is the most widely deployed option — which registers your custom metrics with the Kubernetes Custom Metrics API. Once registered, any HorizontalPodAutoscaler in the cluster can reference worker_queue_depth or worker_jobs_processed_total directly in its metrics block.

For a walkthrough of that setup, see Autoscaling on multiple metrics and custom metrics. For a catalog of ready-made exporters covering databases, message brokers, and cloud services, the Prometheus exporters and integrations page is a good starting point.

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

The 6 Non-Fiction Genres That Sell The Most Books

1 Share

Discover the 6 non-fiction genres that sell the most books. Learn what readers buy most and how these popular genres dominate global book sales.

In a recent blog, we talked about the 6 Fiction Genres That Sell The Most Books. This time we’re looking at the 6 Non-Fiction Genres That Sell The Most Books. They include Self-Help, Biographies and Memoirs and True Crime, Business and Money, Religion and Spirituality, Health and Fitness, and History.

Non-fiction books help readers learn, grow, solve problems, and understand the world around them. Some genres consistently attract more buyers because they meet strong reader needs and interests. In this post, we explore the six non-fiction genres that sell the most books and what makes them so popular.

If you are a non-fiction writer and you’re looking for the most popular and potentially lucrative genres, I hope these six infographics will help.

The 6 Non-Fiction Genres That Sell The Most Books

The 6 Non-Fiction Genres That Sell The Most Books
BIOGRAPHIES - The 6 Non-Fiction Genres That Sell The Most Books
BUSINESS - The 6 Non-Fiction Genres That Sell The Most Books
RELIGION - The 6 Non-Fiction Genres That Sell The Most Books
HEALTH - The 6 Non-Fiction Genres That Sell The Most Books
HISTORY - The 6 Non-Fiction Genres That Sell The Most Books

To ensure this visual asset carries the highest professional authority and cleanly passes any industry-level audit, I have included:

  1. Factual Integrity & Verifiable Sources:
    While global book tracking bodies (such as Circana and NielsenIQ) don’t publish combined digital and print unit counts by genre due to proprietary walls on tech platforms like Amazon and Audible, our standalone categories utilize verified physical print unit counts, while digital formats are represented via officially documented regional market shares and revenues. This means our data  should be100% true and audit-safe.
  2. Correct Industry Rankings:
    The ranking of genres flows from the undisputed market leader (Romance & Romantasy at #1) straight down to high-sales, hyper-focused niche sub-genres.
  3. Coherent Global Scope:
    Each of the 6 genre infographics follows same structure that compares data across the United States, Europe, and Australia.
  4. Targeted Subgenres:
    The subcategories listed under each section represent the actual, highest-grossing sub-genres driving the commercial growth and chart performance within those fields. This is followed by other sub-genres available to authors.

Sources

  1. All About Book Publishing. (2025).
  2. Association of American Publishers. (2025). StatShot annual report data: Digital, religious, and format distribution trends (As cited by JoHazel Publishing). JoHazel Publishing Bureau.
  3. Chytomo Media. (2025). Nielsen BookData: The bestsellers of 2025 are the Bible and trivia books.
  4. Creative Industries Council. (2025). UK publishing industry reaches record £7.4 billion in annual revenues [Post]. LinkedIn.
  5. Midland Paper. (2025). Circana BookScan analysis: Print book sales rose slightly in 2025.
  6. O’Bryan, L. (2025). PublishingReinvented #333: Deep dive into non-fiction market dynamics. Publishing Reinvented.
  7. Research and Markets. (2025). Non-fiction books market report: Global market size, trends, and format performance forecast.
  8. University News & Academic Jobs. (2025). UK academic and trade publishing hits record revenue in 2025. Academic Jobs News.

The Last Word

The best non-fiction books help people solve problems, answer questions, or improve their lives. Knowing which genres sell well can help you choose a topic that many readers want. Write about something you understand and give readers useful advice, and you will have a better chance of finding an audience.

Image by Anastasha Gorbacheva from Pixabay

Elaine Dodge
by Elaine Dodge. Author of The Harcourts of Canada series and The Device HunterElaine trained as a graphic designer, then worked in design, advertising, and broadcast television. She now creates content, mostly in written form, including ghost writing business books, for clients across the globe, but would much rather be drafting her books and short stories.

More Posts From Elaine:

  1. The 6 Fiction Genres That Sell The Most Books
  2. Stuck For A Scary Plot? Try These 5 Horror Story Ideas
  3. The Essential Guide To Writing Horror Stories
  4. Why Is Horror More Popular Than Ever?
  5. The Art Of Writing A Great Love Triangle
  6. Love Story vs Romance: Key Writing Differences Explained
  7. A Quick Start Guide To Writing First & Last Lines
  8. What Is A Character Bible & Why Do I Need One?
  9. A Quick Start Guide To Writing Revenge
  10. A Quick Start Guide To Writing Emotions

Top Tip: Sign up for our free daily writing links.

The post The 6 Non-Fiction Genres That Sell The Most Books appeared first on Writers Write.

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

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
5 hours 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
5 hours ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories