Message-Driven architecture vs REST-APIs

A question I often get: “Do you use message-driven architecture just because REST APIs are slow?” – Well… yes, but actually no. There’s much more behind that decision.

Why use Message-Driven architecture

Cross-device support

The messaging systems allow for real-time communication across servers, desktops, web & mobile apps — all decoupled and event-driven.

Asynchronous communication

REST API forces the sender to wait. Messaging lets you offload heavy work to background consumers while the app stays responsive.

Supports monoliths

The producer-consumer architecture can be used within a single service using in-process events.

Multi-destination delivery

With REST APIs, the sender needs to know the exact endpoint of each receiver. Messaging system publishes to a topic — whoever’s subscribed gets the message. No need to know who or where they are.

Retention

REST API fails if the receiving server is down. Messaging brokers store messages until consumers reconnect. With TTL, you can control how long unconsumed messages are retained before they are discarded.

Fault tolerance

Retries and dead‑letter queues are ideal for systems that favor built‑in recovery mechanisms to reduce the need for manual intervention.

But is messaging suitable for every backend?
No. You should also be aware of the drawbacks.

Drawbacks of MDA

Extra complexity

You need to manage brokers, consumers, retry policies, and observability.

Tracking requests is harder

Messages can fan out to multiple destinations. Tracing who picked up what, when, and what happened next is a challenge.

And because messaging is inherently push‑based…

No immediate response

Publishers don’t wait for a response; the server can’t guarantee instant feedback to user actions.

Eventual consistency

Messaging systems can always send requests, but the downed services need to catch up later. This is not ideal for systems that require strict transactional processes.

Idempotency

The retry mechanism may redeliver the same message when a consumer fails or times out. The consumers must be idempotent to avoid processing the same request multiple times.
Idempotency doesn’t spare REST APIs either.

Every software architecture is a trade‑off. Which direction do you lean into?

If you’re curious to dive deeper into Messaging or Event‑Driven Architecture, I’ve written a handful of articles on the topic.

How to See What Your OpenClaw AI Assistant Actually Costs Per Conversation

I’ve been running OpenClaw for a few months — it’s become my daily AI assistant across WhatsApp and Telegram, handling emails, research, calendar stuff. It’s genuinely great.

But at the end of month one, I opened my Anthropic billing dashboard and saw $43.

I had no idea where it came from. Which conversations? Which agent? The long research session, or just daily chit-chat? No clue.

This is a known issue in the OpenClaw community — there are open feature requests for native token tracking and a CLI usage command that haven’t shipped yet. So I went looking for a workaround.

The Problem With API Billing Dashboards

The Anthropic and OpenAI dashboards show your total spend, but they’re aggregated (no per-conversation breakdown), delayed (often 24+ hours behind), and only model-level (you can see “Claude Sonnet cost $31” but not which feature or session drove that).

If you’re running a personal AI assistant that touches multiple models — Anthropic for complex tasks, a local Ollama model for simple ones — you have zero visibility into what’s costing money vs what’s free.

The Fix: One Import

burn0 is a tiny Node.js library that solves this. It patches fetch and node:http at the runtime level, so it sees every outbound API call your app makes.

npm install @burn0/burn0

Then at the top of your entry file:

import "@burn0/burn0";

Now every API call to Anthropic, OpenAI, Ollama, or any of 50+ other services gets intercepted, and you see real-time cost breakdowns in your terminal:

burn0 > $0.04 today (12 calls) -- anthropic: $0.031 | openai: $0.009

Why This Works Well With OpenClaw

OpenClaw is Node.js — burn0 slots right in.

It tracks local models too. OpenClaw supports Ollama and other local models. burn0 shows these as $0.00, so you can see the real dollar savings from routing locally vs cloud.

It reads actual token counts, not estimates. Token counts come directly from each API response’s metadata — exact numbers, not guesses based on character counts.

Zero changes to OpenClaw. Works at the HTTP layer, no fork needed. When native tracking ships, remove the import.

Privacy-first. Runs entirely locally. Never reads request or response bodies — only metadata. Nothing leaves your machine.

What It Looks Like in Practice

During a typical OpenClaw session:

burn0 > anthropic/claude-sonnet  ->  $0.023  (in: 1847 / out: 312)
burn0 > anthropic/claude-haiku   ->  $0.001  (in: 423 / out: 89)
burn0 > openai/gpt-4o-mini       ->  $0.0004 (in: 156 / out: 44)
burn0 > localhost (ollama)        ->  $0.000  (in: 891 / out: 203)

The Sonnet call cost 23x more than Haiku, and the Ollama call was free. Over a week, this makes it obvious which workflows are worth routing to cheaper models.

Getting Started

npm install @burn0/burn0

Add one line to your entry point and you’re done.

  • GitHub: burn0
  • Website: burn0.dev
  • MIT licensed, free forever, no account required

If you’re running OpenClaw and curious what it’s actually costing you per day, give it a try. Would love to hear what you find.

I Audited My Team’s .env Practices. Here’s What I Found.

Last month I did something uncomfortable: I spent a Friday afternoon auditing how my team actually handles secrets. Not how we say we handle them. How we actually do it.

I checked Slack history, git logs, CI configs, and local machines. What I found wasn’t a disaster — it was worse. It was normal. The kind of normal that every team thinks is fine until it isn’t.

Here’s exactly what I found, and what we did about it.

The audit

Team size: 5 developers, 3 services, 2 environments (staging + production).

I looked at five things:

1. Where do .env files live?

I asked everyone to run find ~ -name ".env" -not -path "*/node_modules/*" on their machines.

Combined results:

  • 23 .env files across 5 laptops
  • 7 of them contained production credentials
  • 2 developers had .env files for projects they left months ago
  • 1 file had a Stripe live key and a database URL on the same line — copy-paste artifact from Slack

The problem isn’t that .env files exist. It’s that they accumulate. Nobody cleans them up. Nobody knows how many copies are out there.

2. How do new devs get credentials?

I searched our Slack for “env”, “password”, “key”, “token”, and “secret”.

  • 14 messages containing actual credentials in the last 6 months
  • 3 of those were in a public channel (we caught that quickly, at least)
  • The onboarding doc said “ask someone for the .env” — no specifics about who, which values, or which environment

The worst part: Slack retains message history. Those credentials are sitting in Slack’s servers forever unless someone manually deletes each one.

3. Are secrets in git history?

# Quick check for common patterns
git log --all -p | grep -E "(API_KEY|SECRET|PASSWORD|TOKEN)=" | head -20

Two hits. Both were “temporary” commits from months ago that were reverted — but the values are still in the history. git revert doesn’t erase data. Anyone who clones the repo gets those secrets.

4. How does CI/CD get secrets?

Our GitHub Actions workflows used repository secrets. Fine in principle, but:

  • 4 people had admin access to the repo (and could read all secrets)
  • No rotation schedule — some secrets hadn’t been updated in over a year
  • No audit log of who accessed what

5. What happens when someone leaves?

We’d had one departure in the past year. Checked the offboarding:

  • GitHub access revoked ✓
  • AWS IAM removed ✓
  • Secrets rotated ✗

Every API key, database password, and service token that developer had access to was still active. For four months.

The scorecard

Practice Status
Secrets encrypted at rest ✗ (plaintext .env files)
Secrets shared securely ✗ (Slack DMs)
Secrets rotated on departure ✗
Secrets rotated on schedule ✗
Git history clean ✗
Least-privilege access ✗
Audit trail ✗

Zero out of seven. And we’re not a careless team. We write tests. We do code review. We just never formalized secrets management because it always felt like a “later” problem.

What we changed

We didn’t do everything at once. Here’s the order that worked for us:

Week 1: Stop the bleeding

Rotated every secret that the departed developer had access to. This took half a day and was painful — but it’s the highest-impact thing you can do.

Deleted secrets from Slack. Searched, found them, deleted each message. Then pinned a message in #engineering: “Never paste credentials here. Not even temporarily.”

Scrubbed git history using BFG Repo-Cleaner:

bfg --replace-text passwords.txt repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive

Then force-pushed and had everyone re-clone. Annoying but necessary.

Week 2: Centralize secrets

We evaluated three options:

  1. HashiCorp Vault — powerful, but needs dedicated ops. We’re 5 people.
  2. Doppler — solid, but more surface area than we needed.
  3. KeyEnv — CLI-first, minimal setup, fit our workflow.

We went with KeyEnv because the migration was zero-friction:

# Import existing .env
keyenv push

# From now on, every developer does:
keyenv pull

# Or skip the file entirely:
keyenv run -- npm start

No code changes. Apps still read process.env. The difference is that secrets come from an encrypted store instead of a file someone pasted in Slack.

Week 3: Lock down access

  • Set up per-environment permissions. Junior devs get development only. Production secrets are admin-restricted.
  • Created service tokens for CI/CD — scoped, rotatable, auditable.
  • Established a quarterly rotation schedule (calendar reminder, nothing fancy).

Week 4: Wrote it down

Added a SECRETS.md to each repo:

## Secrets Management

This project uses KeyEnv for secrets management.

### Setup
1. Get invited to the team: ask in #engineering
2. `keyenv pull` to get development secrets
3. `keyenv run -- npm start` to run with injected secrets

### Rules
- Never commit secrets to git
- Never share secrets via Slack, email, or docs
- Rotate secrets when a team member leaves
- Production secrets require admin access

Run your own audit

You can do this in an afternoon. Here’s the checklist:

  • [ ] Search Slack for credentials (password, key, token, secret, .env)
  • [ ] Count .env files across developer machines
  • [ ] Check git history for leaked values
  • [ ] Review who has access to CI/CD secrets
  • [ ] Verify secrets were rotated after last departure
  • [ ] Check if any service accounts use shared credentials

You’ll probably find what I found: not a catastrophe, but a pile of small risks that compound over time.

The fix doesn’t require enterprise tooling. It requires deciding that secrets management matters, then picking any system that’s better than Slack + .env files.

Have you ever audited your team’s secret practices? I’m curious what others have found — especially the “normal” stuff that everyone does but nobody talks about.

🚀 Building a High-Accuracy Arabic OCR Tool: How I Solved the “Image-to-Text” Challenge

Extraction of text from images (OCR) is a solved problem for Latin languages, but for Arabic, it’s a whole different story. As the developer behind Adawati.app, I spent weeks engineering a solution that doesn’t just “read” Arabic, but understands its complexity.

The Problem: Why Arabic OCR is Hard
Most open-source OCR engines struggle with Arabic for three reasons:

Cursive Nature: Arabic letters change shape based on their position (Start, Middle, End).

Diacritics & Dots: Small dots and marks can change the entire meaning of a word.

Low-Quality Input: Students often take photos of textbooks in poor lighting or at weird angles.

My Engineering Approach
Instead of just “plugging in” a generic API, I built a pipeline focused on Pre-processing and Contextual Inference.

  1. Image Pre-processing (The Secret Sauce)
    Before the AI even looks at the image, I apply several filters:

Binarization: Converting the image to high-contrast black and white to eliminate background noise.

Deskewing: Automatically correcting the angle if the photo was taken tilted.

Noise Reduction: Removing “salt and pepper” noise often found in scanned PDFs.

  1. The AI Engine
    I utilized state-of-the-art deep learning models specifically fine-tuned for Arabic scripts. These models use CNNs (Convolutional Neural Networks) for visual feature extraction and LSTMs (Long Short-Term Memory) to understand the sequence of characters, ensuring that the connected letters are recognized as a coherent word, not just random symbols.

  2. Privacy-First Architecture
    In an era of data harvesting, I made a conscious architectural decision: Zero Retention.

Images are processed in a secure memory buffer.

Once the text is extracted, the image is purged instantly.

No databases, no logs of your documents.

Why I Built This?
I saw students struggling to transcribe their lectures and researchers stuck with non-searchable PDF archives. I wanted to provide a free, fast, and no-login tool that respects their privacy while delivering professional-grade accuracy.

Try it out
If you’re a developer interested in Arabic NLP or a student looking for a reliable tool, check it out here:
👉 Image to Text – Arabic OCR Tool

I’d love to hear your feedback on the accuracy, especially with complex fonts or handwritten notes!

Arabic #OCR #AI #WebDev #Productivity #NextJS

Dify vs n8n: Architectural Roles for Low-Latency AI

Choosing between Dify and n8n isn’t a tool decision—it’s an architectural one that directly impacts your time-to-revenue and operational complexity.

You are not really choosing between two trendy tools. You are choosing between two architectural roles.

That is the right way to think about Dify vs n8n when the real question is: Can either one support serious, low-latency AI applications beyond prototypes?

The answer is yes, but not in the same way.

Dify is closer to an AI application and agent platform. n8n is closer to a workflow and automation engine that can orchestrate AI steps. Both are solid. Both can be part of a fast production stack. But they are optimized for different jobs, and that difference matters the moment you care about response time, concurrency, failure rates, observability, and maintainability under load. Dify positions itself as an open-source platform for building agentic workflows and AI applications, while n8n documents itself as a workflow automation tool with AI capabilities and a scaling model centered on queue mode for larger workloads. read

The short answer

If your primary product is a user-facing AI application with chat, RAG, tools, and agent flows, Dify is often the better front-door choice.

If your primary problem is backend orchestration at scale, with many integrations, many system-to-system actions, and stricter control over throughput and worker behavior, n8n is often the better orchestration choice, especially in queue mode. n8n’s own docs state that queue mode provides the best scalability, and its official benchmark page shows single-instance throughput up to 220 workflow executions per second depending on workflow type and configuration. read

That is why I would not frame this as a winner-takes-all comparison.

I would frame it as a stack design decision.

What Dify is really optimized for

Dify is designed around the AI product layer.

Its official docs describe it as an open-source platform for building agentic workflows, connecting tools and data sources, and deploying AI applications. Dify also provides app-level API access, workflow and chatflow concepts, knowledge pipeline orchestration for ingestion and indexing, and a broader product story around agentic workflows, RAG pipelines, integrations, and observability. read

That matters because low latency is not only about raw backend throughput.

It is also about how much glue you need to build.

When an AI app stack already includes core application primitives like chat-oriented workflows, API publishing, knowledge pipeline orchestration, and integrated tool use, you avoid a lot of extra architecture that would otherwise sit between the user and the model. That often produces a cleaner path to a fast first version.

For low-latency, user-facing AI apps, Dify has three practical advantages:

  1. It is vertically aligned to the AI app UX layer.
    You are closer to the final user experience from day one.

  2. It supports app publishing and API-based integration.
    That gives teams a way to use Dify as both a builder layer and a callable application layer. read

  3. It supports workflow and knowledge pipeline design.
    That matters when RAG latency is the real bottleneck, not model inference alone. read

In practice, Dify performs best when you self-host it close to the model endpoint, vector layer, and core data services, keep tool chains short, and stream responses so the user perceives progress even when the model takes longer to complete. Dify’s docs and product materials support the platform-app-builder framing, but they do not publish the same kind of hard latency benchmark numbers that n8n publishes for webhook and workflow throughput. That is an important distinction. read

What n8n is really optimized for

n8n is optimized around orchestration.

Its docs describe a workflow automation platform with AI capabilities, and its scaling model is explicit: queue mode provides the best scalability, using separate worker instances to process executions. n8n also publishes official performance benchmarks, which is useful because most AI infrastructure debates suffer from vague claims and very little measurable evidence. read

The benchmark page states that n8n can handle up to 220 workflow executions per second on a single instance depending on workflow complexity and configuration. The scalability benchmark blog also reports that when queue mode was enabled, throughput and latency improved materially versus single mode, with the larger c5.4xlarge benchmark showing about 162 requests per second, latency below 1.2 seconds, and zero failures under 200 virtual users for the tested webhook scenario. read

That does not mean n8n is “faster than Dify” in some universal sense.

It means something more useful: n8n gives you a more documented path to throughput-oriented backend scaling.

So for low-latency production work, the architectural lesson is simple:

  • run n8n in queue mode
  • separate webhook intake from worker execution
  • keep critical-path flows short
  • move heavy tasks into async background jobs
  • co-locate your automation engine with your model gateway, Redis, database, and vector services whenever possible

That is how you preserve predictable latency as load increases. n8n’s own documentation is clear that queue mode is the best scalability path for self-hosted production deployments. read

Dify vs n8n: Which is better for low-latency apps?

This is where technical leaders need more precision.

If your goal is a great AI product experience with a native-feeling agent app, retrieval, tool use, and fast iteration at the UX layer, I would lean toward Dify as the primary product-facing layer.

If your goal is a high-throughput backend with many automations, integrations, retries, branching logic, and execution scaling, I would lean toward n8n as the orchestration layer.

That is why these tools are often better as complements than as substitutes.

Here is the architecture pattern I would recommend for many serious teams:

  • Dify as the front-door AI application layer
  • n8n as the backend automation and system orchestration layer
  • model serving, vector storage, and databases deployed close to both
  • non-critical heavy jobs pushed off the synchronous path

That separation reduces architectural confusion. Dify handles what the user sees. n8n handles what the business process needs.

A healthcare example

Imagine you are building a telehealth triage assistant for a provider network.

The end user needs a smooth, low-latency conversational experience. The system needs RAG over clinical protocols, escalation rules, and a controlled tool chain. But it also needs downstream actions: notifications, CRM updates, logging, appointment requests, audit-side event handling, and ETL into other systems.

That is not one problem. That is two.

The cleaner design is:

  • Dify for the triage experience, response streaming, knowledge-grounded reasoning, and application logic closest to the user
  • n8n for the background automations, messaging side-effects, operational notifications, and system integrations

The same pattern carries well into fintech for onboarding and policy assistants, insurtech for claims operations, legal for document triage and internal copilots, and martech for campaign ops or sales enablement copilots.

The domain changes. The architecture discipline does not.

The mistake teams make

The mistake is trying to force one tool to do everything.

If you force n8n to become your complete AI product layer, you can end up building too much scaffolding around the actual user experience.

If you force Dify to become your universal automation backbone, you may still need another layer for broader system orchestration and non-AI workflow control.

That is why this is not just a tooling conversation.

It is an AI architecture conversation.

And that is exactly where AI Strategy Consulting and Digital Transformation Strategy become valuable: deciding where each layer belongs, how the synchronous path stays fast, which actions move off-path, and whether the stack should run on AWS, Azure, GCP, or a sovereign open-source infrastructure depending on data sensitivity, procurement constraints, and operating model.

My take

For most teams building serious AI applications, I would use this decision rule:

  • Choose Dify first when the product is fundamentally an AI application, agent, or RAG-driven experience.
  • Choose n8n first when the product problem is really orchestration, automation, and scale across systems.
  • Use both together when the app needs to feel fast to the user and still coordinate many backend actions reliably.

That is the practical answer.

Not because the tools are equal.

Because they are not designed for the same architectural job.

FAQ

Is Dify good for low-latency AI apps?

Yes, especially when the core need is a user-facing AI app with chat, workflows, RAG, and tools under one platform. Its strength is the application layer, not benchmark-driven backend throughput claims. read

Is n8n better for scale?

For backend workflow scale, n8n has a stronger documented case. Its docs state that queue mode provides the best scalability, and its benchmark pages publish concrete throughput and latency numbers. read

Should I use Dify or n8n for healthcare?

For healthcare, I would usually separate the app layer from the orchestration layer. Dify can power the front-end AI experience, while n8n handles backend automation and side-effects. The final answer depends on your latency target, data boundaries, and integration load.

Can Dify replace n8n?

Sometimes for narrower AI app workflows, but not always. Dify is stronger as an AI app and agent platform. n8n is stronger as a general workflow and automation engine.

Can n8n replace Dify?

Sometimes for backend-heavy use cases, but not when the product requires a polished AI app layer with chat, knowledge flows, and user-facing agent behavior as the center of gravity.

Further Reading

*Written by Dr Hernani Costa | Powered by Core Ventures

Originally published at First AI Movers.

Technology is easy. Mapping it to P&L is hard. At First AI Movers, we don’t just architect systems; we build the ‘Executive Nervous System’ for EU SMEs navigating AI infrastructure decisions.

Is your architecture creating technical debt or business equity?

👉 Get your AI Readiness Score (Free Company Assessment)

Our AI Readiness Assessment evaluates whether your current stack—Dify, n8n, or otherwise—aligns with your business outcomes, data governance, and scaling roadmap.

The Backyard Quarry, Part 2: Designing a Schema for Physical Objects

Diagram showing how a physical rock is represented as a digital record with metadata, images, and a 3D model.

In the first post of this series we set the stage for the Backyard Quarry project.

Once you decide every rock in the yard should have a record, the next question appears immediately:

What exactly should we record?

It’s a deceptively simple question. And like most simple questions in engineering, it opens the door to a surprisingly large number of decisions.

The First Attempt

The most straightforward approach is to keep things minimal.

Each rock gets an identifier and a few attributes.

Something like:

rock_id
size
price

At first glance, this seems reasonable.

We can identify the rock. We can describe it in some vague way. We can assign a price.

But this model breaks down almost immediately.

“Size” is ambiguous. Is that weight? Volume? Longest dimension? All of the above?

Two rocks of the same “size” might behave very differently when you try to move them.

And more importantly, this model doesn’t capture anything about the rock beyond its most basic characteristics.

It’s enough to sell a rock.

It’s not enough to understand one.

Expanding the Model

To make the system more useful, we need to be more explicit.

A slightly richer model might look like this:

rock_id
weight_lb
length_cm
width_cm
height_cm
color
rock_type
location_found
status

Now we’re getting somewhere.

We can distinguish between rocks that look similar but behave differently.

We can track where each rock came from.

We can start to answer questions like:

  • How many rocks do we have in a given area?
  • What size distribution does the dataset have?
  • Which rocks are suitable for different uses?

This is the point where the rock pile starts to feel less like a random collection and more like a dataset.

The Object Data Model

At a higher level, what we’re really doing is separating a physical object into a few distinct components.

Diagram showing how a physical rock is represented as a digital record with metadata, images, and a 3D model.
A simple model for representing a physical object as structured data and associated assets.

Each rock has:

  • metadata describing its properties
  • images representing its appearance
  • optionally, a 3D model capturing its shape

This separation turns out to be important.

Metadata is small, structured, and easy to query.

Images and 3D models are large, unstructured assets that need to be stored and referenced.

Keeping those concerns separate is a pattern that shows up in many real-world systems.

The Identity Problem

Once the schema starts to take shape, another question appears.

How do we uniquely identify a rock?

There are a few options:

  • sequential IDs (rock_001, rock_002)
  • UUIDs
  • physical tags attached to rocks
  • some form of image-based identification

For a small backyard dataset, almost anything works.

But the choice matters more as the system grows.

Sequential IDs are easy to read but require coordination.

UUIDs are globally unique but harder to work with manually.

Physical tags introduce a connection between the digital record and the real-world object.

Even in a simple system, identity becomes a design decision.

Classification: The Quarry Taxonomy

At some point, it becomes useful to introduce categories.

Originally this was just a convenience.

But like many things in this project, it quickly became something more formal.

A simple classification system might look like this:

Class 0 — Pebble
Class 1 — Hand Sample
Class 2 — Landscaping Rock
Class 3 — Wheelbarrow Class
Class 4 — Engine Block Class
Class 5 — Heavy Machinery Class

Each class roughly corresponds to how the rock is handled.

This turns out to be surprisingly useful.

Instead of asking for exact dimensions, we can filter by class:

  • “Show me all Pebble Class rocks”
  • “Exclude anything above Wheelbarrow Class”

In other words, we’ve introduced a derived attribute — something computed from the underlying data rather than stored arbitrarily.

This is exactly how classification systems evolve in real datasets.

Thinking About Lifecycle

Rocks don’t change much physically, but their role in the system does.

A rock might move through states like:

collected
cataloged
listed_for_sale
sold

Tracking this lifecycle introduces another dimension to the data.

Now we’re not just modeling objects.

We’re modeling *objects over *.

Even in a simple system, state and transitions begin to matter.

The Tradeoffs

At this point, the schema is already doing useful work.

But it’s also clear that there’s no perfect design.

Every decision involves tradeoffs:

  • more fields vs simplicity
  • normalized structure vs ease of use
  • flexibility vs consistency

The goal isn’t to design the perfect schema on the first try.

The goal is to design something that can evolve.

Because as soon as we start capturing real data, we’ll learn what we got wrong.

What Comes Next

With a basic schema in place, the next challenge becomes obvious.

We know what we want to store.

Now we need to figure out how to capture it.

In the next post, we’ll look at how to turn a physical rock into images, measurements, and potentially a 3D model — and how that process introduces its own set of constraints.

Because it turns out that collecting data from the physical world is rarely as clean as designing a schema on paper.

Facebooktwitterredditlinkedinmail

The post The Backyard Quarry, Part 2: Designing a Schema for Physical Objects appeared first on Blog of Ken W. Alger.

The Python Community’s Institutional Response to the Astral Acquisition Has Begun

The Python community has been waiting for an institutional answer to a simple question: when OpenAI acquired Astral and inherited control of python-build-standalone, would the PSF respond?

On March 23rd, a CPython core developer posted a quiet but significant update on discuss.python.org. The answer is yes.

What the Post Actually Said

Brett Cannon — long-time CPython core developer, former CPython release manager — posted this in the ongoing Astral acquisition discussion thread (post 49, March 23):

“I’m working on it (and have been since October). It’s been on pause waiting for Astral to upstream their patches after I gave them all an initial review at the end of January. I have a PEP that I’m about to go public with, and once that’s done I’m pivoting back to working on prebuilt, relocatable builds for CPython that will come from python.org.”

The GitHub repository — python/prebuilt-cpython — already exists.

A PEP is ready. The work has been ongoing since October 2025. This isn’t a proposal or a wish. The PSF’s official answer to Astral’s control of Python distribution infrastructure is in progress, and further along than anyone outside the core team knew.

Why This Matters: The python-build-standalone Problem

To understand what’s at stake, you need to know what python-build-standalone is.

python-build-standalone is a project that creates portable, self-contained CPython distributions — Python binaries that work without needing system Python, system libraries, or any external dependencies. You download the binary, it runs. This is what uv uses to install Python versions (the uv python install command). It’s what Rye uses. It’s the foundation that made self-contained Python tooling practical.

Before the OpenAI acquisition, python-build-standalone was controlled by Astral — which meant it was controlled by a well-funded, well-intentioned startup with strong community relationships. After the acquisition, it’s controlled by OpenAI, through Astral, which now belongs to a company whose core business is commercial AI.

This isn’t a catastrophe. OpenAI has given no indication of hostile intent toward Python infrastructure. But the concern is structural, not behavioral: a key piece of Python’s distribution infrastructure now sits under a commercial entity whose interests may not always align with the Python community’s.

The question developers have been asking since March 19: is there an official alternative? Is the PSF building a python-build-standalone equivalent that lives under their governance?

Brett Cannon’s post answers that. Yes. A PEP is coming. python/prebuilt-cpython exists. Python.org will eventually distribute official prebuilt, relocatable CPython builds.

What’s Actually Being Built

The scope is broader than just replacing python-build-standalone.

The python/prebuilt-cpython repo is building:

  • Relocatable CPython builds — move the directory anywhere, it still works (no hardcoded paths)
  • Python.org distribution — official source, not third-party
  • Cross-platform coverage — the same portability guarantees that make python-build-standalone valuable
  • Upstream from the PSF — governance sits with the foundation, not a corporate entity

The PEP will formalize this. Brett gave an initial technical review of Astral’s upstream patches in January 2026. Those patches being upstreamed unblocks the prebuilt-cpython work. The acquisition didn’t kill the upstream collaboration — if anything, Astral’s patches being in-flight is what makes the PSF’s own implementation possible now.

This is worth holding on to: the two tracks (Astral upstreaming patches, PSF building a python.org alternative) are not in conflict. They can both succeed.

What Changes (and What Doesn’t)

What changes: Python will eventually have official prebuilt, relocatable distributions from python.org. Tools that want to use PSF-governed Python binaries — not vendor-governed ones — will have that option. The dependency on python-build-standalone / Astral / OpenAI won’t be mandatory.

What doesn’t change yet:

  • uv still works exactly as before
  • ruff still works exactly as before
  • python-build-standalone still works exactly as before
  • OpenAI acquiring Astral still happened; that governance structure doesn’t change
  • The PEP hasn’t dropped yet — this is pre-announcement

What to watch: When the PEP goes public, it will be the first formal governance document addressing Python distribution independence in the post-acquisition world. It will be referenced in every subsequent discussion about Python toolchain lock-in. Read it.

The Bigger Picture

The Python community’s reaction to the Astral acquisition has been more nuanced than the initial HN threads suggested.

The strongest voices weren’t panicking — they were pattern-matching. “We’ve seen this before. Open-source infrastructure moves under corporate control, governance terms change, community forks happen, maintainers burn out, and two years later we’re migrating again.” Terraform → OpenTofu. Docker Desktop. Elasticsearch. The list is long.

What makes the Brett Cannon post significant isn’t just the technical content — it’s what it represents. A CPython core developer, working since October, building an official alternative, writing a PEP to formalize it. The PSF isn’t watching. They’re building.

The Python community’s institutional response isn’t a tweet thread or a GitHub issue or a strongly-worded blog post. It’s a PEP, a GitHub repo, and a core developer who’s been quietly working on this for five months.

That’s the actual story.

If You’re Building Tooling That Depends on Python Distribution

A few practical notes if this affects your work:

  1. Nothing breaks now. uv, ruff, python-build-standalone — all stable. No migration needed, no urgency.

  2. Watch for the PEP. When Brett’s PEP goes public, read it. It will clarify the intended governance structure, the timeline, and the transition path for tools that want to use python.org-governed Python distributions.

  3. The python/prebuilt-cpython repo is public now. If you want to track the technical work, it’s there.

  4. Community alternatives exist. If you need PSF-governed Python distribution alternatives while the PEP is in progress: python.org installers still work, pyenv works, system package managers work. Tools that need portability and zero-dependency installs don’t have official python.org alternatives yet — that’s what this work is building toward.

The toolchain anxiety is real and worth taking seriously. So is the institutional response.

Brett Cannon’s discuss.python.org post is in the “OpenAI to Acquire Astral” thread, post 49. The prebuilt-cpython work is at github.com/python/prebuilt-cpython.

If you’re building Python tooling and want practical patterns for testing it without depending on third-party subscription services, I write about that — Python Automation Cookbook covers patterns I reach for when building tools that have to work reliably without external service dependencies.

Banks Embrace Tokenized Deposits: The New Frontier of Onchain Cash

Banks Embrace Tokenized Deposits: The New Frontier of Onchain Cash

The financial landscape is undergoing a silent but seismic shift. As the
digital economy matures, traditional banking institutions are no longer
standing on the sidelines of the blockchain revolution. Instead, they are
aggressively pushing into the realm of tokenized deposits , viewing them
as the critical bridge between legacy banking systems and the future of
onchain finance.

What Are Tokenized Deposits?

At their core, tokenized deposits are a digital representation of a bank’s
liability to a customer, recorded on a programmable blockchain ledger. Unlike
stablecoins—which are typically issued by private entities like Circle or
Tether—tokenized deposits are issued by regulated financial institutions. They
represent a claim against the bank, functionally equivalent to a standard
demand deposit, but enhanced by the technological capabilities of distributed
ledger technology (DLT).

The Key Differences: Deposits vs. Stablecoins

  • Regulatory Oversight: Tokenized deposits fall under the umbrella of existing banking regulations and deposit insurance schemes.
  • Issuer Profile: Issued by licensed banks rather than non-bank fintech firms.
  • Settlement Mechanics: These assets are designed for instant, 24/7 settlement on permissioned or public blockchains, bypassing the delays inherent in traditional clearing houses like SWIFT or ACH.

Why Banks Are Racing Toward Onchain Cash

The race to innovate is driven by the urgent need to maintain relevance in a
world where “time is money.” In traditional markets, the settlement of a
transaction can take days. In an onchain ecosystem, settlement is virtually
instantaneous. For a bank, the implications of this shift are profound:

1. Efficiency Gains in Global Settlement

By using tokenized deposits, banks can facilitate cross-border payments with
near-zero latency. This reduces the need for the complex web of correspondent
banking relationships that currently plague international finance.

2. Programmability and Smart Contracts

Because these deposits exist on a blockchain, they can be infused with smart
contracts. This allows for automated conditional payments—such as escrow
services, automated invoice settlement, and complex trade finance
workflows—that execute only when predefined conditions are met.

3. The Liquidity Advantage

Tokenized deposits enable 24/7 liquidity. While traditional banks close their
settlement windows on weekends and holidays, onchain cash operates around the
clock, catering to the needs of modern institutional investors and
decentralized finance (DeFi) platforms.

The Competitive Landscape: Who Is Leading?

The movement is global. From JPMorgan’s Onyx platform to experiments by
Citigroup and various European banks, the appetite for “onchain cash” is
undeniable. Financial institutions are moving beyond pilot programs and are
now exploring how to integrate these assets into their core treasury
management services.

Addressing the Challenges

Despite the optimism, the transition to tokenized deposits is not without
hurdles:

  • Interoperability: Different banks are building on different ledgers. Creating a seamless bridge between these isolated chains is the next big engineering challenge.
  • Legal Certainty: While deposits are legally protected, the legal status of a “token” representing that deposit in a bankruptcy or insolvency scenario requires further clarification from regulators.
  • Privacy and Compliance: Balancing the transparency of a public blockchain with the strict KYC/AML requirements of the banking sector remains a delicate act.

The Future of Banking: Onchain and Programmable

As we look ahead, tokenized deposits appear to be the inevitable evolution of
money. By digitizing the deposit itself, banks are future-proofing their
balance sheets and ensuring they remain the foundation of the financial
system, even as that system migrates to DLT. The shift is not just about
technology; it is about reclaiming the efficiency lost in decades of
fragmented infrastructure.

Frequently Asked Questions

What is the difference between a CBDC and a tokenized deposit?

A Central Bank Digital Currency (CBDC) is a digital liability of the central
bank, whereas a tokenized deposit is a digital liability of a commercial bank.
They serve different roles in the broader financial architecture.

Are tokenized deposits safe?

Generally, yes. They are backed by the same bank balance sheets as traditional
deposits and typically fall under the same regulatory protections, such as
FDIC insurance in the United States.

How does this impact retail customers?

Initially, this shift will primarily impact institutional, wholesale, and
corporate finance. However, as the technology matures, retail banking apps may
eventually allow users to interact with tokenized deposits to facilitate
faster international transfers or interest-earning smart contract accounts.

Conclusion

The intensified focus on tokenized deposits signals a turning point for the
banking industry. By embracing onchain cash, banks are positioning themselves
to lead in the digital era, ensuring that the legacy systems of the past can
coexist with the programmable, decentralized finance of the future. The race
is on, and the institutions that master this technology will define the next
generation of financial services.

How to Setup Codacy: Complete Step-by-Step Guide (2026)

Codacy screenshot

What is Codacy and why use it?

Codacy is an automated code quality and security platform that analyzes your code for bugs, vulnerabilities, code smells, duplication, and complexity across 49 programming languages. It integrates directly with GitHub, GitLab, and Bitbucket to scan every pull request and provide actionable feedback before code reaches your main branch.

Instead of assembling a stack of separate tools for linting, SAST, SCA, secrets detection, and coverage tracking, Codacy bundles all of these into a single platform with a unified dashboard. For teams that want comprehensive code quality enforcement without managing multiple tools and CI configurations, this consolidation is the primary appeal.

This guide walks through every step of setting up Codacy from scratch – creating your account, connecting your repositories, configuring code patterns, enabling PR analysis, setting quality gates, uploading code coverage, writing a .codacy.yml configuration file, managing your team, and troubleshooting common issues. By the end, you will have a fully configured Codacy setup that scans every pull request automatically and enforces your team’s quality standards.

Codacy screenshot

Prerequisites

Before you begin the setup process, make sure you have the following ready:

  • A GitHub, GitLab, or Bitbucket account with admin access to the repositories you want to analyze
  • At least one repository with code in a supported language (Codacy supports 49 languages including JavaScript, TypeScript, Python, Java, Go, Ruby, PHP, C#, Kotlin, and Swift)
  • If you plan to upload code coverage, a CI pipeline (GitHub Actions, GitLab CI, CircleCI, Jenkins, or similar) that runs your test suite

The setup process itself takes under ten minutes for a single repository. Configuring code patterns, quality gates, and coverage reporting adds another 20-30 minutes depending on how much customization you need.

Step 1: Create your Codacy account

Navigate to app.codacy.com and click the sign-up button. Codacy supports sign-up through three Git providers:

  • GitHub – recommended if your repositories are on GitHub
  • GitLab – for GitLab cloud or self-managed instances
  • Bitbucket – for Bitbucket Cloud or Data Center

Select your Git provider and authorize Codacy to access your account. For GitHub, this involves two authorization steps:

  1. OAuth authorization – grants Codacy permission to read your profile and email address
  2. GitHub App installation – grants Codacy access to your repositories, webhooks, and pull request status checks

When installing the GitHub App, you can choose to grant access to all repositories in your organization or select specific repositories. If you are evaluating Codacy, start with one or two repositories rather than your entire organization.

After authorization, Codacy redirects you to your dashboard. If you signed up with a GitHub organization, Codacy automatically creates a Codacy organization that mirrors your GitHub org structure.

Step 2: Connect and add your first repository

From your Codacy dashboard, click the “Add repository” button in the top navigation. Codacy displays a list of all repositories it has access to, filtered by your Git provider organization.

Select the repository you want to add and click “Add.” Codacy immediately begins the initial analysis, which involves:

  1. Cloning the repository (Codacy accesses the code temporarily for analysis and does not store your source code permanently)
  2. Detecting the programming languages present in the repository
  3. Running the default analysis tools for each detected language
  4. Generating an initial quality report

The first analysis typically takes between 2 and 15 minutes depending on the repository size. You can monitor the progress from the repository dashboard page.

Once the initial analysis completes, Codacy displays your repository’s quality metrics:

  • Grade – an overall letter grade (A through F) based on the aggregated quality metrics
  • Issues – the total number of issues found, categorized by severity (error, warning, info) and type (security, error-prone, performance, code style, compatibility, unused code)
  • Complexity – file-level and function-level complexity scores
  • Duplication – the percentage of duplicated code blocks
  • Coverage – code coverage percentage (requires separate setup, covered in Step 7)

Step 3: Understand the Codacy dashboard

The repository dashboard is your central hub for monitoring code quality over time. It is worth spending a few minutes understanding its layout before diving into configuration.

The dashboard displays several key panels:

Quality evolution chart shows how your quality grade and issue count have changed over the past days, weeks, or months. This is useful for tracking whether your team’s code quality is improving or degrading over time.

Open issues panel lists all currently open issues grouped by category. You can filter by severity, category, language, and analysis tool to focus on what matters most. Each issue links to the specific file and line number where it was found.

Pull request analysis panel shows recent pull requests with their quality status – whether they passed or failed your quality gates, how many new issues were introduced, and what the coverage delta was.

File quality panel ranks your files by the number of issues, complexity, and coverage. This is useful for identifying the most problematic files in your codebase and prioritizing cleanup work.

Step 4: Configure code patterns

Code patterns are the rules that Codacy uses to analyze your code. Each supported language has one or more analysis tools, and each tool has a set of rules (called patterns) that can be individually enabled or disabled.

To configure code patterns:

  1. Navigate to your repository in Codacy
  2. Click Code patterns in the left sidebar
  3. You will see a list of analysis tools grouped by language

For a JavaScript/TypeScript repository, you might see tools like ESLint, JSHint, and Codacy’s own analyzers. For Python, you might see Pylint, Bandit, Prospector, and Ruff. For Java, you might see PMD, SpotBugs, and Checkstyle.

Enabling and disabling tools

Each analysis tool has a toggle switch. If you already use a specific linter locally (for example, ESLint with a custom configuration), you may want to disable Codacy’s built-in ESLint to avoid conflicts. Alternatively, you can configure Codacy to use your repository’s existing ESLint configuration file.

Configuring individual patterns

Click on any tool to expand its rule list. Each rule shows:

  • Rule name and description – what the rule checks for
  • Severity – error, warning, or info
  • Category – security, error-prone, performance, code style, compatibility, or unused code
  • Toggle – enable or disable the rule

For a new setup, the recommended approach is to start with the default patterns enabled and then tune them based on your first analysis results. If the initial analysis produces too many low-value findings, disable the noisiest patterns first (typically code style patterns for legacy codebases).

Using your existing linter configuration

If your repository already has a linter configuration file (.eslintrc.js, pylintrc, .rubocop.yml, etc.), you can configure Codacy to use it instead of Codacy’s default patterns:

  1. Go to Code patterns and select the relevant tool
  2. Enable the “Use the configuration file in my repository” toggle
  3. Codacy will use your existing configuration file for that tool

This is particularly useful for teams that have already invested time in customizing their linter rules and do not want to duplicate that work in the Codacy UI.

Step 5: Setup PR analysis

Pull request analysis is one of Codacy’s most valuable features because it catches issues before they reach your main branch. When configured, Codacy automatically analyzes every pull request and reports:

  • New issues introduced by the PR
  • Issues that were fixed by the PR
  • Coverage changes (increase or decrease)
  • Complexity changes
  • Whether the PR passes or fails your quality gates

How PR analysis works

When you open a pull request on a connected repository, Codacy’s GitHub App receives a webhook notification. It then:

  1. Analyzes only the files changed in the PR (not the entire repository)
  2. Compares the results against the target branch to identify new versus existing issues
  3. Posts a status check on the PR (pass or fail based on quality gates)
  4. Optionally posts a summary comment on the PR with detailed findings

PR analysis is enabled by default when you connect a repository through the GitHub App. To verify it is working, open a pull request on your connected repository and check for the Codacy status check.

Configuring PR status checks

To customize what Codacy reports on your pull requests:

  1. Navigate to your repository in Codacy
  2. Click Settings in the left sidebar
  3. Scroll to the Pull Requests section

Here you can configure:

  • Status check behavior – whether to post a single combined status check or separate checks for issues, coverage, and duplication
  • PR comment – whether to post a summary comment on the PR, and what information to include
  • Annotations – whether to add inline annotations on specific lines in the PR diff

Making Codacy status checks required

To prevent pull requests from being merged when they fail Codacy’s quality gates, configure branch protection rules in your Git provider:

For GitHub:

  1. Go to your repository settings on GitHub
  2. Click Branches in the sidebar
  3. Click Add branch protection rule (or edit an existing rule for your main branch)
  4. Under “Require status checks to pass before merging,” search for and select the Codacy status checks
  5. Save the rule
Branch name pattern: main

[x] Require status checks to pass before merging
    Required status checks:
      - Codacy Static Code Analysis

For GitLab:

  1. Go to your project settings
  2. Navigate to Merge Requests
  3. Under “Merge checks,” enable “Pipelines must succeed”
  4. Codacy reports its results as a pipeline job via the GitLab integration

Step 6: Configure quality gates

Quality gates define the minimum quality standards that your code must meet. They apply both at the repository level (tracking overall quality trends) and at the pull request level (blocking merges that degrade quality).

To configure quality gates:

  1. Navigate to your repository in Codacy
  2. Click Quality settings in the left sidebar
  3. Set thresholds for each metric

Available quality gate metrics

Metric Description Recommended Starting Threshold
New issues Maximum number of new issues allowed in a PR 0 critical, 5 total
Coverage Minimum coverage on new code 60% (adjust based on your current baseline)
Coverage variation Maximum allowed coverage decrease -1%
Duplication Maximum duplication in new code 3%
Complexity Maximum file complexity allowed No threshold initially

Setting practical thresholds

The key to effective quality gates is starting with thresholds your team can actually meet. If your current code coverage is 40%, setting a quality gate at 80% coverage will cause every PR to fail and your team will quickly disable the gate.

A practical rollout strategy:

  1. Week 1-2: Set quality gates to observe mode – track but do not block. Look at how your PRs perform against the default thresholds
  2. Week 3-4: Enable blocking quality gates with achievable thresholds – zero new critical issues and coverage must not decrease
  3. Month 2+: Gradually tighten thresholds as your team adapts – increase minimum coverage, reduce allowed new issues

Repository-level quality gates

In addition to PR-level gates, Codacy tracks quality at the repository level. The repository quality gate evaluates the overall codebase against thresholds you set for total issues, overall coverage, and overall duplication. This is useful for tracking long-term quality trends and setting goals for codebase cleanup.

Step 7: Setup code coverage with the Coverage Reporter

Code coverage tracking in Codacy requires uploading coverage reports from your CI pipeline. Codacy does not run your tests – it processes coverage reports generated by your existing test suite and displays the results alongside other quality metrics.

Step 7a: Generate a coverage report

First, make sure your test suite generates a coverage report in one of the supported formats:

  • LCOV – common for JavaScript/TypeScript (Istanbul, c8, nyc)
  • Cobertura XML – common for Python (pytest-cov, coverage.py) and Java (JaCoCo with Cobertura output)
  • JaCoCo – standard for Java/Kotlin
  • Clover – used by PHP (PHPUnit) and some Java projects
  • OpenCover – common for .NET projects

For example, in a JavaScript project using Vitest:

{
  "test": {
    "coverage": {
      "provider": "v8",
      "reporter": ["lcov", "text"]
    }
  }
}

Or in a Python project using pytest:

pytest --cov=src --cov-report=xml:coverage.xml

Step 7b: Get your project API token

  1. Navigate to your repository in Codacy
  2. Click Settings in the left sidebar
  3. Scroll to the Coverage section (or look under Integrations)
  4. Copy the Project API Token – you will need this to authenticate the upload

Store this token as a secret in your CI environment:

  • GitHub Actions: Add it as a repository secret named CODACY_PROJECT_TOKEN
  • GitLab CI: Add it as a CI/CD variable named CODACY_PROJECT_TOKEN
  • CircleCI: Add it as an environment variable

Step 7c: Add the Coverage Reporter to your CI pipeline

For GitHub Actions:

name: Tests and Coverage

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

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci

      - name: Run tests with coverage
        run: npm test -- --coverage

      - name: Upload coverage to Codacy
        uses: codacy/codacy-coverage-reporter-action@v1
        with:
          project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
          coverage-reports: coverage/lcov.info

For GitLab CI:

test:
  stage: test
  image: node:20
  script:
    - npm ci
    - npm test -- --coverage
  after_script:
    - bash <(curl -Ls https://coverage.codacy.com/get.sh) report
        -r coverage/lcov.info
        --project-token $CODACY_PROJECT_TOKEN
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

For Python projects with GitHub Actions:

name: Tests and Coverage

on:
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: pip install -r requirements.txt pytest pytest-cov

      - name: Run tests with coverage
        run: pytest --cov=src --cov-report=xml:coverage.xml

      - name: Upload coverage to Codacy
        uses: codacy/codacy-coverage-reporter-action@v1
        with:
          project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
          coverage-reports: coverage.xml

Using the standalone CLI reporter:

If you prefer not to use the GitHub Action, you can use the Coverage Reporter CLI directly:

# Download the reporter
curl -Ls https://coverage.codacy.com/get.sh -o codacy-coverage-reporter.sh

# Upload the report
bash codacy-coverage-reporter.sh report 
  -r coverage/lcov.info 
  --project-token $CODACY_PROJECT_TOKEN

Uploading multiple coverage reports

If your project has multiple test suites or generates coverage reports for different components (common in monorepos), you can upload them as partial reports and finalize them:

# Upload first report as partial
bash <(curl -Ls https://coverage.codacy.com/get.sh) report 
  -r coverage/unit.lcov.info 
  --partial 
  --project-token $CODACY_PROJECT_TOKEN

# Upload second report as partial
bash <(curl -Ls https://coverage.codacy.com/get.sh) report 
  -r coverage/integration.lcov.info 
  --partial 
  --project-token $CODACY_PROJECT_TOKEN

# Finalize the coverage
bash <(curl -Ls https://coverage.codacy.com/get.sh) final 
  --project-token $CODACY_PROJECT_TOKEN

Once coverage is uploaded, Codacy displays coverage metrics on your repository dashboard and includes coverage changes in every PR analysis.

Step 8: Create a .codacy.yml configuration file

The .codacy.yml file gives you version-controlled configuration for your Codacy analysis. Instead of configuring everything through the Codacy UI, you can define your analysis settings in a file that lives in your repository root – making the configuration reviewable, consistent, and part of your standard code review process.

Basic .codacy.yml structure

Create a file named .codacy.yml in your repository root:

---
engines:
  eslint:
    enabled: true
    config: .eslintrc.js
  stylelint:
    enabled: true
  csslint:
    enabled: false
  duplication:
    enabled: true
    config:
      minimum_tokens: 50
  metrics:
    enabled: true

languages:
  javascript:
    extensions:
      - .js
      - .jsx
      - .mjs
  typescript:
    extensions:
      - .ts
      - .tsx

exclude_paths:
  - "node_modules/**"
  - "dist/**"
  - "build/**"
  - "coverage/**"
  - "**/*.min.js"
  - "**/*.bundle.js"
  - "vendor/**"
  - "public/assets/**"
  - ".next/**"

Enabling tools with custom configuration files

If your repository already has linter configuration files, reference them in .codacy.yml:

---
engines:
  eslint:
    enabled: true
    config: .eslintrc.json
  pylint:
    enabled: true
    config: .pylintrc
  rubocop:
    enabled: true
    config: .rubocop.yml
  checkstyle:
    enabled: true
    config: checkstyle.xml

When you specify a config path, Codacy uses your repository’s linter configuration instead of its default patterns. This ensures that Codacy’s analysis matches what developers see when running linters locally.

Complete .codacy.yml example for a full-stack project

Here is a comprehensive configuration for a typical full-stack project with JavaScript, Python, and Docker:

---
engines:
  eslint:
    enabled: true
    config: .eslintrc.json
  pylint:
    enabled: true
    config: .pylintrc
  bandit:
    enabled: true
  prospector:
    enabled: false
  duplication:
    enabled: true
    config:
      minimum_tokens: 70
  metrics:
    enabled: true
  hadolint:
    enabled: true
  shellcheck:
    enabled: true
  markdownlint:
    enabled: false

languages:
  javascript:
    extensions:
      - .js
      - .jsx
      - .mjs
  typescript:
    extensions:
      - .ts
      - .tsx
  python:
    extensions:
      - .py

exclude_paths:
  - "node_modules/**"
  - "dist/**"
  - "build/**"
  - "coverage/**"
  - "**/*.min.js"
  - "**/*.bundle.js"
  - "**/*.generated.*"
  - "vendor/**"
  - "migrations/**"
  - "static/vendor/**"
  - "tests/fixtures/**"
  - "**/__snapshots__/**"
  - ".github/**"
  - "docs/**"

When to use .codacy.yml vs. the UI

Use .codacy.yml when:

  • You want configuration to be version-controlled and reviewable in PRs
  • Multiple team members need to adjust analysis settings
  • You have multiple repositories and want consistent configuration
  • You want to reference existing linter configuration files

Use the Codacy UI when:

  • You are evaluating Codacy and experimenting with different settings
  • You need to quickly toggle rules without committing code
  • You want to use Codacy’s visual rule browser to explore available patterns

Note that .codacy.yml settings override UI settings when both are present. If you define an engine in .codacy.yml, the UI configuration for that engine is ignored.

Step 9: Team management and permissions

Managing team access in Codacy follows your Git provider’s organization structure. When you connect an organization from GitHub, GitLab, or Bitbucket, Codacy mirrors the membership automatically.

How team syncing works

Codacy pulls team membership from your Git provider. When a developer joins your GitHub organization and has access to connected repositories, they automatically appear in Codacy. When someone leaves the organization, their access is revoked.

You do not need to manually invite every team member. However, you can invite members directly from Codacy if they have not yet logged in:

  1. Go to your Organization settings in Codacy
  2. Click People in the sidebar
  3. Click Add people and enter their email address or Git provider username

Role-based access control

Codacy provides three permission levels:

Role Permissions
Admin Full control – manage organization settings, billing, integrations, add/remove repositories, configure all tools and quality gates
Manager Repository management – configure code patterns, quality gates, and PR settings for repositories they have access to. Cannot manage billing or organization settings
Developer View-only for configuration – can view dashboards, issues, and analysis results. Cannot change code patterns, quality gates, or repository settings

To change a team member’s role:

  1. Go to Organization settings and click People
  2. Find the member in the list
  3. Click the role dropdown and select the new role

Managing repository access

By default, Codacy syncs repository access from your Git provider. If a developer has read access to a repository on GitHub, they can view that repository’s analysis in Codacy. If they have write access, they can configure analysis settings (depending on their Codacy role).

You can also restrict which repositories are visible in Codacy by adjusting the GitHub App permissions:

  1. Go to your GitHub organization settings
  2. Click GitHub Apps in the sidebar
  3. Click Configure next to the Codacy app
  4. Under “Repository access,” select specific repositories instead of “All repositories”

Step 10: Ignoring files and directories

Not every file in your repository needs analysis. Generated code, vendored dependencies, build outputs, test fixtures, and third-party assets will produce noise that clutters your dashboard and obscures real issues.

Method 1: Using the Codacy UI

  1. Navigate to your repository in Codacy
  2. Click Settings in the left sidebar
  3. Click Ignored files
  4. Add glob patterns for files and directories to exclude

Common patterns to add:

**/node_modules/**
**/vendor/**
**/dist/**
**/build/**
**/.next/**
**/coverage/**
**/*.min.js
**/*.min.css
**/*.bundle.js
**/*.generated.*
**/migrations/**
**/fixtures/**
**/__snapshots__/**
**/*.lock
**/package-lock.json
**/yarn.lock

Method 2: Using .codacy.yml

Add an exclude_paths section to your .codacy.yml file (as shown in Step 8). This method is preferred because the exclusions are version-controlled.

Method 3: Tool-specific ignores

Some analysis tools respect their own ignore files. For example:

  • ESLint reads .eslintignore
  • Pylint reads ignore patterns from .pylintrc
  • Bandit reads .bandit configuration

If you configure Codacy to use your repository’s linter configuration files, these tool-specific ignore patterns are respected automatically.

Step 11: Advanced configuration

Setting up Codacy with GitHub Actions as a standalone check

While Codacy’s GitHub App handles most analysis automatically, you can also run the Codacy CLI in your own CI pipeline for more control:

name: Codacy Analysis

on:
  pull_request:
    branches: [main]

jobs:
  codacy-analysis:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Codacy Analysis CLI
        uses: codacy/codacy-analysis-cli-action@master
        with:
          project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
          upload: true
          max-allowed-issues: 0

This approach is useful when you want Codacy analysis to run as part of your existing CI pipeline rather than as a separate webhook-triggered process.

Configuring the Codacy AI Reviewer

Codacy’s AI Reviewer uses large language models to provide contextual feedback on pull requests beyond what static analysis rules can catch. To configure it:

  1. Navigate to your repository in Codacy
  2. Click Settings in the left sidebar
  3. Look for the AI Reviewer or AI features section
  4. Enable the AI Reviewer toggle

The AI Reviewer posts additional comments on your PRs that cover logic issues, potential bugs, and improvement suggestions that static rules would not catch. Note that the AI Reviewer is available on paid plans (Team and Business).

Configuring SAST and secrets detection

Codacy includes built-in SAST (Static Application Security Testing) and secrets detection:

SAST scanning is enabled by default and uses security-focused rules from tools like Bandit (Python), SpotBugs with FindSecBugs (Java), and Codacy’s own security analyzers. Security issues appear in the Issues panel with a “Security” category tag.

Secrets detection scans for hardcoded credentials, API keys, and tokens. It is enabled at the organization level:

  1. Go to Organization settings
  2. Click Security or Integrations
  3. Enable Secrets detection

When Codacy detects a potential secret in a pull request, it flags it as a critical security issue and blocks the PR if your quality gates are configured to block on critical issues.

Troubleshooting common issues

Even with a straightforward setup, you may encounter issues. Here are the most common problems and their solutions.

Codacy is not analyzing my pull requests

Check the GitHub App permissions. Go to your GitHub organization settings, click “GitHub Apps,” and verify that the Codacy app has access to the repository in question. If you recently changed the app’s repository access, Codacy may need a webhook re-delivery.

Check the repository status. In Codacy, navigate to your repository and look at the status indicator. If analysis is paused or errored, click into the status for details. Common causes include rate limiting (too many concurrent analyses) or a misconfigured default branch.

Verify webhook delivery. Go to your GitHub repository settings, click “Webhooks,” find the Codacy webhook, and check the “Recent Deliveries” tab. Look for failed deliveries (non-200 status codes) and redeliver them.

Coverage is not showing up

Verify the upload succeeded. Check your CI pipeline logs for the Codacy Coverage Reporter output. A successful upload prints a confirmation message with the commit SHA. If the upload fails, the most common causes are an incorrect project token or a malformed coverage report.

Check the commit SHA. Codacy matches coverage reports to specific commits. If your CI pipeline runs on a merge commit (common with GitHub Actions pull_request events), the SHA may not match the PR head. Use the --commit-uuid flag to specify the correct SHA:

bash <(curl -Ls https://coverage.codacy.com/get.sh) report 
  -r coverage/lcov.info 
  --commit-uuid ${{ github.event.pull_request.head.sha }} 
  --project-token $CODACY_PROJECT_TOKEN

Check the coverage report format. Codacy supports LCOV, Cobertura, JaCoCo, Clover, and OpenCover. If your report is in a non-standard format or is malformed, the upload may succeed but Codacy will not process it. Validate your coverage report locally before uploading.

Too many false positives

Disable noisy tools. If a specific analysis tool produces mostly irrelevant findings for your codebase, disable it in Code patterns. You can always re-enable it later after addressing higher-priority issues.

Use your own configuration files. Enable the “Use configuration file in repository” option for tools that support it. Your team’s existing linter configuration reflects patterns that have already been tuned for your codebase.

Exclude generated and vendored code. Files that are not authored by your team (generated code, vendored dependencies, migration files) should be excluded from analysis. See Step 10 for exclusion methods.

Start with high-severity rules only. In Code patterns, sort rules by severity and disable info-level and some warning-level rules initially. Focus on error-level rules that catch actual bugs and security vulnerabilities.

Analysis is slow

Check your repository size. Codacy’s initial analysis processes your entire repository, which takes longer for large codebases. Subsequent PR analyses only process changed files and should be significantly faster.

Reduce the number of enabled tools. Each analysis tool adds processing time. If you have ten tools enabled but only use three languages, disable the tools for languages not present in your repository.

Exclude large directories. Build output directories, vendored dependencies, and asset directories add files to the analysis queue without providing value. Exclude them using .codacy.yml or the Codacy UI.

Quality gate is too strict or too lenient

Review the gate thresholds. Navigate to Quality settings and check each threshold. If every PR is failing, your thresholds may be unrealistic for the current state of your codebase. If no PR ever fails, your thresholds are too lenient.

Use the “new code” focus. Configure quality gates to evaluate only new code introduced in the PR rather than the entire repository. This prevents legacy code issues from blocking new work and lets your team maintain quality standards going forward without requiring a full codebase cleanup first.

Best practices for ongoing Codacy usage

After the initial setup, maintaining your Codacy configuration requires periodic attention to keep it effective.

Review new issues weekly. Assign a rotating team member to triage new Codacy findings each week. Dismiss false positives with a reason, assign real issues to the appropriate developer, and adjust patterns when a rule consistently produces unhelpful feedback.

Update quality gate thresholds quarterly. As your codebase improves, tighten the thresholds to maintain pressure for quality improvement. If coverage started at 40% when you onboarded Codacy and has risen to 55%, update the minimum coverage threshold to 50% so it continues to serve as a meaningful gate.

Keep your .codacy.yml in sync with your linter configs. When you update your ESLint configuration locally, make sure Codacy is set to use the repository’s configuration file rather than its own copy. This prevents drift between what developers see locally and what Codacy reports on PRs.

Use Codacy’s organization-level settings for consistency. If you have multiple repositories, configure shared settings (default quality gates, default tool configurations, default exclusion patterns) at the organization level. Individual repositories can override these defaults when needed, but starting from a shared baseline keeps things consistent.

Monitor the Codacy status page. Codacy occasionally experiences service disruptions that affect analysis processing times. Check status.codacy.com if analyses are taking unusually long or not completing.

Summary

Setting up Codacy involves ten main steps: creating your account, connecting a repository, understanding the dashboard, configuring code patterns, enabling PR analysis, setting quality gates, uploading code coverage, creating a .codacy.yml configuration file, managing team permissions, and configuring file exclusions. The initial setup takes under 30 minutes for a single repository, and the ongoing maintenance is minimal once you have tuned the analysis to match your team’s standards.

Codacy’s biggest advantage is consolidation – it replaces the need for separate tools for linting, SAST, SCA, secrets detection, coverage tracking, and duplication analysis. For teams that want a single dashboard for code quality without assembling and maintaining a multi-tool stack, Codacy provides a compelling solution at a reasonable price point.

For more details on Codacy’s pricing tiers and how it compares to alternatives, see our Codacy pricing breakdown and Codacy alternatives guide.

Frequently Asked Questions

How do I setup Codacy for my GitHub repository?

Sign up at app.codacy.com using your GitHub account, authorize the Codacy GitHub App, select the repositories you want to analyze, and Codacy will automatically run its first analysis. The entire process takes under five minutes. Once connected, Codacy scans every new pull request and commits to your default branch automatically.

Is Codacy free to use?

Yes, Codacy offers a free Developer plan that includes the AI Guardrails IDE extension for VS Code, IntelliJ, Cursor, and Windsurf. For team features like PR analysis, quality gates, dashboards, and code coverage tracking, you need the Team plan at $18/dev/month (annual billing). Open-source projects get the full Team plan for free.

How do I connect Codacy to GitHub?

Go to app.codacy.com and click Sign up with GitHub. Authorize the Codacy OAuth app to read your profile, then install the Codacy GitHub App on your organization or personal account. Select the repositories you want Codacy to access. Codacy creates webhooks automatically so that every push and pull request triggers analysis.

How do I configure Codacy code patterns?

Navigate to your repository in Codacy, click Code patterns in the left sidebar, and you will see all available analysis tools listed by language. Each tool shows its individual rules which you can toggle on or off. You can also switch entire tools on or off. For bulk configuration, use a .codacy.yml file in your repository root to define which tools and rules to enable.

How do I setup Codacy PR analysis?

PR analysis is enabled by default when you connect a repository through the GitHub App integration. Codacy automatically posts a status check and summary comment on every pull request showing new issues, coverage changes, and quality gate status. You can customize what appears in the PR comment from your repository’s Settings page under Pull Requests.

How do I add code coverage to Codacy?

Install the Codacy Coverage Reporter in your CI pipeline using the bash script or CLI binary. Generate a coverage report in your test suite (LCOV, Cobertura, JaCoCo, or Clover format), then upload it with the codacy-coverage-reporter tool using your project API token. Codacy will display coverage metrics on your dashboard and in PR comments.

What is the .codacy.yml file used for?

The .codacy.yml file is a repository-level configuration file that lets you define which analysis tools and patterns are enabled, exclude files and directories from analysis, set language-specific overrides, and configure coverage settings. It lives in your repository root and overrides the Codacy UI settings, making configuration version-controlled and consistent across your team.

How do I ignore files in Codacy?

You can ignore files in Codacy through two methods. In the UI, go to your repository Settings, click Ignored files, and add glob patterns like /test/, /vendor/, or */.min.js. Alternatively, add an exclude_paths section to your .codacy.yml file. Both methods prevent Codacy from analyzing matching files and directories.

How do I setup Codacy quality gates?

Go to your repository in Codacy, click Quality settings in the sidebar, then configure thresholds for issues (new and total), complexity, duplication, and code coverage. Quality gates apply to both the overall repository and individual pull requests. When a PR fails a quality gate, Codacy reports a failing status check on the PR, which you can configure to block merges through branch protection rules.

Does Codacy work with GitLab and Bitbucket?

Yes, Codacy supports GitHub, GitLab (cloud and self-managed), and Bitbucket (cloud and Data Center). The setup process is similar across all platforms – sign up with your Git provider account, authorize the integration, and select repositories. PR analysis, quality gates, and coverage tracking work on all three platforms.

How do I add team members to Codacy?

Team members are added automatically when they join your Git provider organization and have access to repositories connected to Codacy. You can also invite members manually from the Organization settings page in Codacy. Role-based access control lets you assign Admin, Manager, or Developer roles with different permission levels for configuring tools, managing repositories, and viewing dashboards.

Why is Codacy not analyzing my repository?

The most common causes are: the repository language is not supported, the GitHub App does not have permission to access the repository, the repository is empty or has no analyzable files, or analysis is paused. Check the repository status page in Codacy for error messages. If the GitHub App permissions were recently changed, you may need to re-authorize from your GitHub organization settings.

How long does Codacy take to analyze a repository?

The initial full analysis typically takes 2-15 minutes depending on repository size, number of files, and languages used. Subsequent PR analyses are faster because Codacy only scans changed files, usually completing in 1-3 minutes. Very large repositories with hundreds of thousands of lines may take longer on the first scan.

Can I use Codacy with monorepos?

Yes, Codacy supports monorepos. You can configure different analysis tools and patterns for different directories within the same repository using the .codacy.yml file. File exclusion patterns let you scope analysis to specific services or packages. Coverage can be uploaded separately for each component and Codacy will aggregate the results.

Originally published at aicodereview.cc