Retrieval vs Representation in Knowledge Systems

Most modern knowledge systems optimize retrieval, and that is understandable.
Search is visible, easy to demo, and feels magical when it works. Type a question, get an answer.

But retrieval is only one half of the problem. The deeper question is:

What shape does the knowledge have before anything tries to retrieve it?

That is representation — the structure behind the knowledge:

  • notes
  • pages
  • schemas
  • graphs
  • entities
  • relationships
  • summaries
  • taxonomies
  • source boundaries
  • canonical versions

Retrieval asks:

Can I find something relevant?

Representation asks:

Is the knowledge organized in a way that makes sense?

These are not the same problem. A RAG system with poor representation becomes a fast interface to a messy archive. It can retrieve fragments, but it cannot fix broken structure. It can quote documents, but it cannot decide which one is canonical. It can assemble context, but it cannot guarantee that the underlying knowledge is coherent.

This is why LLM Wiki style systems are interesting: they shift effort from query time to ingest time. Instead of only retrieving chunks when a user asks a question, they attempt to pre-structure knowledge into pages, concepts, summaries, and links. That does not make RAG obsolete — it means retrieval and representation are different layers, and good knowledge systems need both.

The core distinction

Retrieval is about access; representation is about meaning.

Layer Question Examples
Retrieval How do I find the right information? search, embeddings, BM25, reranking, vector stores
Representation How is knowledge structured? notes, wikis, graphs, schemas, ontologies
Reasoning How do I use the knowledge? synthesis, comparison, inference, decision making

A weak system often jumps straight to retrieval; a strong system first asks:

  1. What are the core concepts?
  2. What is the canonical source?
  3. What relationships matter?
  4. What changes over time?
  5. What should be retrieved?
  6. What should already be represented?

This is the difference between search over documents and an actual knowledge system.

Why retrieval became dominant

Retrieval became dominant because it maps well to the modern AI stack. A typical RAG pipeline looks like this:

  1. Load documents
  2. Split them into chunks
  3. Generate embeddings
  4. Store vectors
  5. Retrieve relevant chunks
  6. Optionally rerank them
  7. Put them into an LLM prompt
  8. Generate an answer

This pipeline is practical: it is relatively easy to build, works with messy documents, scales to large corpora, avoids retraining models, and gives LLMs access to current information. That is why RAG became the default pattern for “AI over documents.”

But there is a trap:

RAG improves access to knowledge. It does not automatically improve the knowledge.

If your content is duplicated, outdated, contradictory, badly chunked, or poorly named, retrieval will surface those problems — often with confidence.

What representation means

Representation is the way knowledge is shaped before retrieval happens. It answers questions like:

  • Is this knowledge stored as documents, notes, entities, or facts?
  • Are relationships explicit or implicit?
  • Are there canonical pages?
  • Are there summaries?
  • Are concepts linked?
  • Is the system organized by topic, workflow, time, or ownership?
  • Can a human maintain it?
  • Can a machine reason over it?

Representation is not decoration — it determines what kind of operations are possible.

Forms of representation

Documents

Documents are the most common representation. Examples include:

  • articles
  • PDFs
  • manuals
  • reports
  • README files
  • support pages
  • blog posts

Documents are easy for humans to write, but they are often hard for machines to use because they mix facts, narrative, context, examples, opinions, outdated sections, and repeated explanations into the same container. Documents are good containers, but they are not always good knowledge structures.

Notes

Notes are more flexible than documents. They can be:

  • atomic
  • linked
  • private
  • unfinished
  • concept focused

A note system, such as a PKM or second brain, can represent evolving knowledge better than a polished document repository. Good notes capture thinking in progress; bad notes become an unsearchable junk drawer.

Wikis

Wikis represent knowledge as maintained pages. A good wiki has:

  • stable pages
  • clear topics
  • internal links
  • ownership
  • canonical answers
  • update patterns

A wiki is stronger than a loose document dump because it gives knowledge a home. “Deployment checklist” lives in one place. “Incident response” lives in one place. “RAG architecture” lives in one place. That matters because retrieval works better when knowledge has a stable structure.

Knowledge graphs

Knowledge graphs represent knowledge as entities and relationships. Instead of storing only text, they model things like:

  • Person works on Project
  • Model supports ContextLength
  • Page depends on Concept
  • Service connects to Database
  • Tool implements Protocol

Graphs are powerful because relationships become explicit, which helps with traversal, dependency analysis, entity resolution, lineage, reasoning, and recommendations. But graphs are expensive to maintain and they are not magic — a bad graph is just structured confusion.

Schemas and ontologies

Schemas define expected structure; ontologies go further and define types, relations, and constraints. They answer:

  • What kinds of things exist?
  • What properties do they have?
  • How can they relate?
  • What rules apply?

This is useful when correctness matters, such as in medical knowledge, legal knowledge, enterprise data catalogs, product taxonomies, and compliance systems. The tradeoff is rigidity: the more formal the representation, the more expensive it is to evolve.

LLM-generated representations

Modern systems increasingly use LLMs to create representations. Examples include:

  • summaries
  • extracted entities
  • topic pages
  • concept maps
  • synthetic FAQs
  • document outlines
  • cross-links
  • glossary entries

This is where LLM Wiki style systems sit. They use the model not only to answer queries but to pre-process and structure knowledge before the query happens. RAG says “retrieve relevant chunks at query time”; LLM Wiki says “compile useful knowledge structures at ingest time.” Both patterns can coexist in the same architecture.

What retrieval means

Retrieval is the process of finding relevant information. Common retrieval methods include:

  • keyword search
  • full text search
  • vector search
  • hybrid search
  • metadata filtering
  • graph traversal
  • reranking
  • query rewriting
  • agentic search

Retrieval is not one thing — it is a layered stack of complementary methods.

Keyword search

Keyword search matches terms and is still useful because it is predictable, debuggable, fast, and good for exact terms, IDs, error messages, names, and code. Its weakness is semantic mismatch: if the user searches “how to stop repeated answers” but the document says “presence penalty”, keyword search may miss the best result.

Vector search

Vector search retrieves by semantic similarity. It is useful when:

  • wording differs
  • concepts are fuzzy
  • users ask natural language questions
  • documents use inconsistent terminology

Its weakness is precision — vector search can retrieve things that feel related but are not actually correct, which is especially risky in technical systems.

Hybrid search

Hybrid search combines keyword and vector retrieval, which is often better than either alone. Keyword search catches exact matches; vector search catches conceptual matches. For technical knowledge bases, hybrid retrieval is usually a strong default.

Reranking

Reranking takes an initial set of retrieved results and reorders them using a stronger model. This improves quality because the first retrieval step is often broad. A typical pattern retrieves 50 chunks, reranks to the top 5 or 10, then passes only the best context to the LLM. Reranking is one of the most practical ways to improve RAG quality.

Agentic retrieval

Agentic retrieval turns search into a process. Instead of one query, an agent may:

  1. Ask an initial question
  2. Search
  3. Inspect results
  4. Reformulate the query
  5. Search again
  6. Compare sources
  7. Synthesize an answer

This is closer to research than search. It is useful for complex questions, but it is slower and harder to control.

Retrieval without representation is fragile

A retrieval system can only retrieve what exists. It cannot reliably fix:

  • unclear concepts
  • duplicate pages
  • inconsistent terminology
  • stale documentation
  • missing source ownership
  • contradictory statements
  • weak internal linking
  • bad document boundaries

This is the most common mistake in RAG projects: teams build a vector database and expect it to become a knowledge system. A vector database is not a knowledge architecture — it is an access layer.

Representation without retrieval is isolated

The opposite failure also exists. You can have a beautifully structured knowledge base that nobody can find. This happens with:

  • over-designed wikis
  • deep folder trees
  • rigid taxonomies
  • poorly indexed documentation
  • private note systems with no discovery
  • graphs without usable interfaces

Representation gives knowledge structure; retrieval gives knowledge reach. You need both.

The tradeoff map

Speed vs coherence

Retrieval is fast to build and representation takes longer. If you need a prototype, retrieval wins; if you need long-term trust, representation matters more.

Priority Better starting point
Fast Q&A over many docs Retrieval
Stable technical knowledge Representation
Exploratory research PKM plus retrieval
Enterprise assistant Structured corpus plus RAG
Agent memory Representation plus selective retrieval

A pure RAG prototype can be built quickly, but a reliable knowledge system takes curation.

Flexibility vs consistency

Loose documents are flexible; structured knowledge is consistent. Flexibility helps when:

  • the domain changes quickly
  • knowledge is incomplete
  • users are exploring
  • the system is personal

Consistency helps when:

  • multiple people rely on it
  • answers must be trusted
  • workflows depend on it
  • AI systems consume it

The more people or agents depend on knowledge, the more representation matters.

Recall vs precision

Retrieval systems often optimize recall first, which means finding anything that might be relevant. But good answers need precision, which means finding the best evidence rather than merely related evidence. Representation improves precision by making concepts and boundaries clearer — a well-structured page is easier to retrieve accurately than a random paragraph buried inside a long document.

Ingest-time cost vs query-time cost

RAG usually pushes work to query time. At query time, the system:

  • rewrites the query
  • retrieves chunks
  • reranks results
  • assembles context
  • asks the model to reason over fragments

LLM Wiki style systems push more work to ingest time. At ingest time, the system:

  • reads sources
  • extracts concepts
  • writes summaries
  • creates pages
  • links related ideas
  • maintains structure
Architecture Expensive step Benefit
RAG Query time Flexible retrieval
LLM Wiki Ingest time Pre-compiled structure
Knowledge graph Modeling time Explicit relationships
Wiki Maintenance time Canonical knowledge

None of these is universally better — they optimize different costs.

Why LLM Wiki exists

LLM Wiki exists because retrieval alone often repeats work. In a normal RAG system, every query may force the model to interpret raw fragments again:

  1. Retrieve chunks about a topic
  2. Ask the LLM to infer the concept
  3. Generate an answer
  4. Forget the synthesis
  5. Repeat next time

LLM Wiki says:

Stop re-deriving the same synthesis. Compile it.

Instead of only storing raw documents, it creates structured pages that summarize and connect knowledge, which can improve coherence, reuse, token efficiency, human readability, and long-term maintenance. But it has a cost: the system must maintain the wiki, and if the wiki is wrong, stale, or hallucinated, the structure becomes dangerous.

RAG hallucination vs bad representation

People often blame the LLM when a RAG system gives a bad answer, and sometimes that is correct. But many failures are actually retrieval or representation failures.

Failure mode 1. Correct document, wrong chunk

The answer exists, but chunking splits it badly. The model receives:

  • half of a paragraph
  • missing context
  • a table without explanation
  • a definition without constraints

The LLM fills those gaps, which looks like hallucination, but the deeper problem is broken representation.

Failure mode 2. Related chunk, wrong answer

Vector search retrieves something semantically similar but operationally wrong. The query asks about production deployment; the retrieved chunk discusses local development. The terms overlap but the meaning differs, so the model answers with local setup instructions for a production problem. This is retrieval imprecision.

Failure mode 3. Conflicting sources

Two documents disagree — one old, one new. The retrieval system returns both, and the LLM merges them into a confident but invalid answer. This is not just a retrieval problem but a representation problem, because the knowledge base lacks canonical state.

Failure mode 4. No concept model

The system has many documents but no model of the domain. It does not know that:

  • “agent memory” differs from “RAG”
  • “wiki” differs from “PKM”
  • “embedding search” differs from “full text search”
  • “deployment” differs from “hosting”

Without conceptual representation, retrieval becomes fuzzy matching.

Failure mode 5. Generated structure becomes fake authority

LLM Wiki systems have their own failure mode. If an LLM generates a clean page from bad sources, the result can look more authoritative than the original material. This is dangerous: a polished hallucination is worse than a messy source document. Any generated representation needs:

  • source links
  • review
  • update rules
  • confidence markers
  • ownership

Design implications

Optimize retrieval when the corpus is large and dynamic

Retrieval should be the priority when:

  • the corpus is huge
  • documents change frequently
  • users ask many unpredictable questions
  • you need broad coverage
  • perfect structure is unrealistic

Examples: support knowledge bases, enterprise document search, research assistants, internal chat over many files, legal discovery, and customer service bots. In these cases, invest in strong retrieval:

  • hybrid search
  • metadata filters
  • reranking
  • query rewriting
  • source citation
  • evaluation sets

Optimize representation when coherence matters

Representation should be the priority when:

  • knowledge must be trusted
  • answers must be consistent
  • concepts are reused often
  • the domain has clear structure
  • multiple systems depend on it

Examples: architecture knowledge, product documentation, compliance rules, API references, operational runbooks, curated research collections, and technical blog clusters. In these cases, invest in:

  • canonical pages
  • glossary terms
  • diagrams
  • internal links
  • ownership
  • versioning
  • review cadence

Optimize both when AI systems depend on knowledge

If an AI agent depends on the knowledge, retrieval alone is usually not enough. Agents need:

  • stable context
  • clear task rules
  • durable memory
  • structured references
  • source boundaries
  • update behavior

For agentic systems, representation becomes part of system design. A coding agent does not only need to retrieve “some docs” — it needs to know:

  • project conventions
  • architecture decisions
  • command patterns
  • forbidden dependencies
  • testing workflow
  • deployment rules

Some of that belongs in RAG, some belongs in memory, and some belongs in structured project documentation.

Practical decision framework

If the problem is finding information

Optimize retrieval. Examples:

  • “Find relevant pages.”
  • “Answer questions over documents.”
  • “Search across many PDFs.”
  • “Locate similar support tickets.”

Use:

  • full text search
  • vector search
  • hybrid retrieval
  • reranking
  • metadata filtering

If the problem is making knowledge coherent

Optimize representation. Examples:

  • “Create a canonical explanation.”
  • “Resolve duplicate pages.”
  • “Define the domain model.”
  • “Build a stable knowledge base.”

Use:

  • wiki pages
  • concept maps
  • taxonomies
  • knowledge graphs
  • summaries
  • schemas

If the problem is repeated synthesis

Use compiled representation. Examples:

  • “We answer the same conceptual questions repeatedly.”
  • “The system keeps re-summarizing the same sources.”
  • “We need a stable synthesis layer.”

Use:

  • LLM Wiki
  • curated summaries
  • topic pages
  • human-reviewed generated pages

If the problem is adaptive continuity

Use memory. Examples:

  • “The agent should remember user preferences.”
  • “The coding agent should remember project conventions.”
  • “The assistant should continue work across sessions.”

Use:

  • agent memory
  • preference stores
  • episodic memory
  • semantic memory
  • project memory

How this applies to a technical blog

A technical blog can be more than a sequence of posts — it can become a represented knowledge system. Articles are documents, categories are weak taxonomy, internal links are graph edges, pillar pages are canonical summaries, series pages are curated pathways, and search is retrieval. If you only publish isolated posts, retrieval has to work harder. If you build strong representation, retrieval becomes easier.

That means:

  • clear cluster boundaries
  • stable slugs
  • canonical pages
  • comparison pages
  • glossary-style explainers
  • internal links
  • structured metadata

This is why site architecture matters — not just for SEO, but because it is knowledge representation. The Knowledge Management cluster on this site is itself an example of representation-first publishing.

How this applies to RAG

RAG quality depends heavily on representation. A well-structured source corpus improves:

  • chunk quality
  • retrieval accuracy
  • citation quality
  • answer consistency
  • evaluation clarity

Before building a complex RAG pipeline, ask:

  1. Are the source documents current?
  2. Are duplicates removed?
  3. Are important concepts clearly named?
  4. Are pages scoped correctly?
  5. Are tables and code blocks retrievable?
  6. Are canonical answers obvious?
  7. Are document boundaries meaningful?

If the answer is no, better embeddings will only help so much.

How this applies to LLM Wiki

LLM Wiki is a representation-first pattern. It is useful when:

  • the corpus is small or medium sized
  • knowledge is stable enough to summarize
  • repeated synthesis is expensive
  • humans benefit from readable pages
  • you want structure before retrieval

It is less useful when:

  • the corpus is massive
  • content changes constantly
  • freshness is more important than coherence
  • governance is weak
  • generated summaries cannot be reviewed

LLM Wiki is not a replacement for RAG but a different layer, and a strong system can use both:

  1. LLM Wiki creates structured summaries.
  2. RAG retrieves from raw sources and wiki pages.
  3. Human review keeps the representation trustworthy.

Suggested architecture patterns

Pattern 1. Retrieval first

Use when speed matters.

documents
  -> chunks
  -> embeddings
  -> retrieval
  -> LLM answer

Good for:

  • prototypes
  • broad search
  • large corpora
  • early experiments

Weakness: coherence depends on source quality.

Pattern 2. Representation first

Use when trust matters.

sources
  -> curated pages
  -> internal links
  -> maintained knowledge base
  -> search or RAG

Good for:

  • documentation
  • technical knowledge
  • long-term content
  • team knowledge

Weakness: requires maintenance.

Pattern 3. Compiled knowledge

Use when repeated synthesis matters.

raw sources
  -> LLM extraction
  -> generated summaries
  -> topic pages
  -> reviewed knowledge base
  -> retrieval

Good for:

  • LLM Wiki systems
  • research collections
  • personal knowledge bases
  • stable domains

Weakness: generated structure must be audited.

Pattern 4. Hybrid knowledge architecture

Use when building serious systems.

raw documents
  -> structured knowledge layer
  -> search index
  -> retrieval and reranking
  -> AI answer
  -> feedback and maintenance

Good for:

  • production RAG
  • internal knowledge systems
  • AI assistants
  • technical publishing systems

Weakness: more moving parts.

Evaluation questions

To evaluate retrieval, ask:

  • Did the system find the right source?
  • Did it rank the right source highly?
  • Did it retrieve enough context?
  • Did it avoid irrelevant context?
  • Did the answer cite the correct source?

To evaluate representation, ask:

  • Is the knowledge structured clearly?
  • Is there a canonical page?
  • Are concepts named consistently?
  • Are relationships explicit?
  • Is the content maintained?
  • Can humans and machines both use it?

Do not evaluate a knowledge system only by answer quality — a good answer can hide a bad structure.

The opinionated rule

If your system fails occasionally, improve retrieval. If it fails repeatedly in the same conceptual area, improve representation.

Bad retrieval misses the right information. Bad representation means the right information does not really exist in a usable shape.

Conclusion

Retrieval and representation solve different problems: retrieval gives access, representation gives structure. RAG is powerful because it makes external knowledge available to LLMs at query time, but RAG does not automatically make knowledge coherent, canonical, or maintained. That is why wikis, PKM systems, knowledge graphs, and LLM Wiki style systems still matter.

The future is not retrieval vs representation but layered knowledge systems:

  • representation for structure
  • retrieval for access
  • memory for continuity
  • reasoning for synthesis

If you are building a serious knowledge system, do not start with the vector database. Start with the shape of the knowledge, then decide how it should be retrieved.

Sources and further reading

Avoid Unnecessary Re-renders in Vue with `v-memo`

Vue School Link

When building Vue applications, performance usually feels great by default – Vue’s reactivity system is incredibly efficient.

But as applications grow larger, you may eventually run into situations where:

  • components re-render too often
  • large lists become expensive
  • UI updates feel sluggish
  • unnecessary computations happen repeatedly

This is where memoization becomes useful. And in Vue, one of the easiest ways to optimize rendering is with v-memo.

In this article, we’ll explore:

  • What memoization is
  • What problem it solves
  • How v-memo works in Vue
  • Real-world examples
  • Best practices and common mistakes

Let’s dive in.

🤔 What Is Memoization?

Memoization is an optimization technique where results are cached and reused instead of recalculating them again.

Instead of:

  • recomputing
  • re-rendering
  • recalculating

You simply use the cached version.

🟢 What Problem Does Memoization Solve?

In reactive applications, updates can trigger many re-renders even when most data has not changed for example:

  • Parent component updates
  • Entire list re-renders
  • Hundreds of child components update unnecessarily

This can hurt performance, responsiveness, and even battery usage on mobile devices.

Without memoization every update causes new rendering work – even for unchanged content.

With memoization Vue can skip rendering when dependencies stay the same which reduces DOM operations, Virtual DOM diffing, and in general component work.

🟢 What Is v-memo in Vue?

v-memo is a Vue directive that memoizes part of a template.

Basic example:

<div v-memo="[value]">
  {{ expensiveContent }}
</div>

Vue will only re-render this block when value changes. Otherwise Vue reuses the previous rendered result.

Let’s imagine a large user list.

Without v-memo

<script setup lang="ts">
const users = ref([
  { id: 1, name: 'John' },
  { id: 2, name: 'Alice' }
])

const counter = ref(0)
</script>

<template>
  <button @click="counter++">
    {{ counter }}
  </button>

  <div
    v-for="user in users"
    :key="user.id"
  >
    {{ user.name }}
  </div>
</template>

Every counter update re-renders the whole list even though users never changed.

With v-memo we can:

<script setup lang="ts">
const users = ref([
  { id: 1, name: 'John' },
  { id: 2, name: 'Alice' }
])

const counter = ref(0)
</script>

<template>
  <button @click="counter++">
    {{ counter }}
  </button>

  <div
    v-for="user in users"
    :key="user.id"
    v-memo="[user.id]"
  >
    {{ user.name }}
  </div>
</template>

And now counter updates still happen but unchanged list items are skipped. This becomes extremely valuable for large lists, dashboards, or tables.

🟢 When NOT to Use It

v-memo is not needed everywhere as Vue is already highly optimized.

Avoid using it for tiny components, static content, or premature optimization as overusing memoization can:

  • reduce readability
  • make debugging harder
  • introduce stale UI bugs

🧪 Best Practices

  • Use v-memo only for measurable bottlenecks
  • Great for large dynamic lists
  • Keep dependency arrays minimal
  • Avoid premature optimization
  • Profile performance before optimizing
  • Combine with virtual scrolling for huge datasets
  • Prefer stable keys and predictable state updates

📖 Learn more

If you would like to learn more about Vue, Nuxt, JavaScript or other useful technologies, checkout VueSchool by clicking this link or by clicking the image below:

Vue School Link

It covers most important concepts while building modern Vue or Nuxt applications that can help you in your daily work or side projects 😉

🧪 Advance skills

A certification boosts your skills, builds credibility, and opens doors to new opportunities. Whether you’re advancing your career or switching paths, it’s a smart step toward success.

Check out Certificates.dev by clicking this link or by clicking the image below:

Certificates.dev Link

Invest in yourself—get certified in Vue.js, JavaScript, Nuxt, Angular, React, and more!

✅ Summary

Memoization is a powerful optimization technique that helps avoid unnecessary work.

In this article, you learned:

  • What memoization is
  • What problem it solves
  • How Vue’s v-memo works
  • Practical examples for lists and components
  • When to use it — and when not to

Take care!
And happy coding as always 🖥️

From Closures to an AST in a Kotlin Transform Graph

kumulant is a streaming statistics library: you feed it numbers, it maintains an accumulator like a mean or a quantile sketch, and you read snapshots back. Above the accumulator sits a graph of transforms and filters that preprocesses each value before it lands in the stat: filter out negatives, log-transform latencies, take a weighted dot product of a feature vector.

The first version of that graph was Kotlin lambdas all the way down. Pre-update transforms were (Double) -> Double, filters were (Double) -> Boolean, paired transforms were (Double, Double) -> Pair<Double, Double>. A schema would look something like this (the StatSchema / by stat pattern is from the previous post):

object LatencyMetrics : StatSchema() {
    val p99 by stat(
        DDSketch(probabilities = doubleArrayOf(0.99))
            .filter { it >= 0 }
            .transform { ln(it) }
    )
}

This is the path of least resistance in Kotlin and it works fine while the only caller is in-process Kotlin code. The lambdas are typed, the call site is short, and the closure captures whatever it needs from the enclosing scope.

The wire problem

kumulant’s job inside the Eignex rewrite is to back a cloud-deployed monitoring layer. The expected caller is a service that wants to author its stat config as YAML and POST it over HTTP, not link kumulant as a Kotlin dependency. Once you’ve decided that’s the deployment shape, every closure in the graph is a problem. A (Double) -> Double doesn’t serialize. You can’t write a transform in YAML if transform is a JVM lambda.

The naive fix is to ship a handful of named transforms (log, sqrt, negate) and let YAML reference them by string. That works until the first user needs log(x) minus log(y) or a piecewise expression, at which point you either keep adding named cases or invent a tiny expression language. Better to invent it up front.

The AST

The redesign turns every closure-shaped slot in the graph into a sealed AST:

@Serializable
sealed interface ScalarExpr {
    fun eval(x: Double, y: Double = 0.0, v: DoubleArray = EMPTY_VECTOR): Double
}

@Serializable @SerialName("X")     data object X : ScalarExpr { ... }
@Serializable @SerialName("Const") data class Const(val v: Double) : ScalarExpr { ... }
@Serializable @SerialName("Mul")   data class Mul(val l: ScalarExpr, val r: ScalarExpr) : ScalarExpr { ... }
@Serializable @SerialName("Log")   data class Log(val a: ScalarExpr) : ScalarExpr { ... }
@Serializable @SerialName("VFold") data class VFold(val op: VFoldOp) : ScalarExpr { ... }
// ... Add, Sub, Div, Neg, Abs, Exp, Sqrt, Pow, Min, Max, IfExpr, VDot, V(index)

Mirror the same shape for BoolExpr (Gt, Lt, And, Or, Not, InRange, etc.) and VectorExpr for the cases where the output is a vector of arbitrary length, not a scalar. Each node is @Serializable with a @SerialName discriminator, so kotlinx.serialization round-trips the whole tree polymorphically. The leaves X, Y, and V(i) are placeholders that get bound to the current input when eval runs.

The call site you’d want to keep, transform { ln(it) }, would now have to read transform(Log(X)). Doable, but losing the operator syntax is a real regression. Kotlin’s operator overloading recovers it:

operator fun ScalarExpr.plus(rhs: ScalarExpr): ScalarExpr = Add(this, rhs)
operator fun ScalarExpr.times(rhs: Double): ScalarExpr = Mul(this, Const(rhs))
infix fun ScalarExpr.gt(rhs: Double): BoolExpr = Gt(this, Const(rhs))
// ... one per operator, three handfuls in total

With those in scope, the user-facing API looks almost identical to the lambda version. What’s underneath is the difference:

val p99 by stat(
    DDSketch(probabilities = doubleArrayOf(0.99))
        .filter(X gt 0.0)        // BoolExpr: Gt(X, Const(0.0))
        .transform(Log(X))       // ScalarExpr: Log(X)
)

That same schema serializes to YAML as a tree the user can hand-edit or templated by a deploy pipeline.

What you lose, what you gain

The loss is real: you can’t drop into arbitrary Kotlin in the body of a transform. If your transform isn’t expressible as a composition of the AST node types you’ve defined, you have to add a node. There’s no escape hatch to a raw lambda for the YAML path, because the whole point is that the YAML path doesn’t have a JVM to run a closure on the other side.

In exchange:

  • The config serializes to YAML or JSON without thinking about it.
  • The AST is inspectable, so you can diff two versions of a schema and tell a user what changed before a redeploy.
  • The runtime cost is still a closure call per node, but you can compile the AST down to a single closure at materialize time and amortize the tree walk; kumulant does this in its spec layer.
  • Adding a new node type is one data class, one eval impl, one serial name. Adding a new built-in to a named-transforms registry would be the same amount of code with worse composability.

The lesson I took away: as soon as a config has to cross the wire, the wire format isn’t a serialization concern bolted on the side of the typed API, it’s the thing the typed API has to match. Starting with closures and trying to bolt YAML on later would have meant two sources of truth and a translation layer between them; starting from the AST and letting Kotlin’s operator overloading recover the ergonomics gave both surfaces from one definition.

The expression node code above lives in Eignex/kumulant under schema/Expr.kt. The serialization plumbing (polymorphic discriminator, typed-key schemas) is the Eignex/skema library, covered in more detail in the previous post.

Automate Your Boring Dev Tasks in CLAUDE.md: A Practical Walkthrough

You’re explaining the same thing to Claude every single session. Your project structure. Your error handling pattern. The fact that you never use default exports. The fact that src/migrations/ is sacred ground and nothing in there gets touched without a dry run.

Every time. From scratch. Like the last conversation never happened.

That’s not a Claude problem. That’s a CLAUDE.md problem — specifically, not having one that actually does anything.

The File Most People Get Wrong

Here’s what most CLAUDE.md files look like in the wild:

This is a Next.js 14 app using TypeScript and Tailwind. 
We use Prisma for the database. Follow clean code principles.

That’s a context dump. It’s better than nothing. It’s also burning most of the file’s real potential.

CLAUDE.md isn’t a README for Claude. It’s an instruction set. The difference matters because Claude doesn’t just read it — it shapes how Claude behaves for every prompt that follows. Everything you define there runs implicitly, in the background, without you asking.

Think about what that actually means. You can encode a full pre-commit workflow and have it trigger automatically. You can define conditional behavior based on branch name, file path, annotation tags. You can build macros — two-word commands that kick off four operations and hand you back a consolidated status report.

Most people use 5% of that surface. This is the other 95%.

Two things to know before you start: the file lives in your project root, and you can also have a global one at ~/.claude/CLAUDE.md that applies across every project. Local overrides global where they conflict. Claude reads the whole file every session but weights the top more heavily as context compresses in long runs — so put the critical stuff first, not buried at the bottom after your stack overview.

Encoding Workflows That Run Themselves

Pick the task you repeat most. The one where you open a new Claude session, type out the same three-step process, watch it execute, and think: I’ve done this exact thing forty times this month.

That task belongs in CLAUDE.md as a standing instruction.

Say your commit cycle always hits the same sequence: lint, type-check, coverage check, changelog entry. Instead of narrating it every time:

## Pre-Commit Workflow

Before finalizing any code change:
1. Run `npm run lint --fix` and surface errors that couldn't be auto-fixed
2. Run `npx tsc --noEmit` and report type errors
3. Run `jest --coverage --testPathPattern=<modified_file>` — flag if coverage drops below 80%
4. Append a one-line entry to CHANGELOG.md under today's date

Do not prompt me to confirm each step. Run sequentially and report results together.

You’ve removed four context-switches from your commit cycle. Claude runs the sequence, hands you one consolidated status block, and you make a call. No back-and-forth. No “should I proceed with step 2?” interruptions mid-flow.

That last line — “do not prompt me to confirm each step” — is load-bearing. Without it, Claude asks permission at every stage. Sometimes that’s what you want. If you trust the sequence, skip the ceremony.

Making Claude Understand Your Architecture, Not Just Your Stack

There’s a version of this that goes deeper than workflow. Your codebase has conventions — accumulated decisions that reflect how your team actually thinks about structure. Claude defaults to what “most projects” do. That’s often wrong for you specifically.

Stop re-explaining it:

## Code Generation Standards

- Named exports only. Never default exports.
- Error handling: Result<T, E> pattern from `~/lib/result.ts`. Never throw.
- Database queries live in `~/server/db/queries/`. Never inline SQL in route handlers.
- API responses must match the schema in `~/types/api.ts` — reference it before generating any response shape.
- One component per file. Filename matches component name exactly.

Now you’re not just telling Claude what framework you’re using. You’re handing it a mental model of how your codebase is organized and what rules are non-negotiable. When it generates a new route handler or a database query, it follows your architecture because your architecture is in the file.

The specificity of these rules scales directly with how much you can trust the output without review. Loose rules, more review. Tight rules, faster merge. Write them the way you’d write them for a sharp junior engineer who needed the reasoning, not just the rule.

Documentation That Doesn’t Rot

Documentation maintenance is one of those tasks everyone agrees is important and nobody wants to do. The docs drift. Behavior changes, comments don’t. Six months later someone is reading a JSDoc that describes a function that no longer exists in the form it describes.

The fix isn’t a documentation sprint. It’s making docs a mandatory side effect of code changes:

## Documentation Policy

When modifying any exported function, class, or type:
- Update the JSDoc comment to reflect current behavior
- If the function appears in docs/api.md, update that entry
- If the change is breaking, add a deprecation notice before implementing

When adding a new environment variable:
- Add it to .env.example with a descriptive comment
- Add it to the "Environment Variables" section of README.md

The shift here is subtle but significant. You’re not scheduling documentation — you’re building it into the cost of every change. The docs stay accurate because they’re maintained incrementally, at the same moment the code changes, by the same process that changes the code.

No deferred cleanup sprint. No “we’ll fix the docs later” that never happens.

Protecting the Parts of the Codebase That Deserve It

Some directories are not like the others. Auth logic, database migrations, payment processing — these are the areas where a casual refactor or a misunderstood instruction can cause damage that’s genuinely hard to undo.

You can encode that sensitivity directly:

## File-Specific Rules

`src/auth/`: Treat all changes here as security-sensitive. Flag anything that:
- Modifies token generation or validation logic
- Changes session duration or cookie attributes
- Adds or removes permission checks

Flag before implementing. Do not proceed without explicit confirmation.

`src/migrations/`: Never modify existing migration files. New migrations only.
Always run `npx prisma migrate dev --dry-run` before proposing any migration and include the full output.

The auth block adds a friction layer that makes you stop and think before a change goes through — which is the actual goal. The migrations block encodes a hard rule that eliminates a class of mistakes completely. Both of these things used to live in your head, in a wiki page nobody reads, or in a PR review comment that comes too late.

Two-Word Macros for Multi-Step Operations

Claude Code runs shell commands. Your CLAUDE.md can tell it exactly when and which ones — and you can give that sequence a name.

## External Tool Integration

After modifying any Python file:
- Run `black <file>` and `isort <file>` before presenting changes
- Run `mypy <file>` — include any type errors in your response

When I say "full sync":
- Run `git fetch --all`
- Run `pip install -r requirements.txt`
- Run `alembic upgrade head`
- Run `pytest -x -q`
- Report the state of each step in sequence

“Full sync” is a macro. Two words in, four operations out, one status report back. If you’ve got a Raspberry Pi 5 [AFFILIATE: Raspberry Pi 5] sitting on your desk as a local dev server, this kind of CLAUDE.md setup is what separates it from being a novelty to being something that actually runs your environment for you.

The pattern generalizes. Name any multi-step operation you run more than twice a week and give Claude a trigger phrase for it. You’ll find yourself reaching for those phrases more than you expect.

Conditional Behavior: Claude That Reads the Room

Static instructions are powerful. Conditional instructions are smarter.

## Context-Aware Behavior

If I'm working in a branch prefixed with `hotfix/`:
- Skip style enforcement
- Skip changelog updates
- Prioritize correctness and minimal diff over everything else

If the file contains `// @legacy`:
- Do not refactor without explicit request
- Do not apply modern patterns
- Preserve existing logic structure exactly

The hotfix rule exists because production is down and nobody cares about your naming conventions right now. Fast and correct is the only thing that matters. The legacy annotation is a signal that the code is old on purpose — there’s context around why it is the way it is, and Claude shouldn’t helpfully modernize it because you didn’t ask.

Both rules encode judgment calls you were already making manually. Writing them into the file means Claude makes them with you, automatically.

The CLAUDE.md That Maintains Itself

This one takes a minute to click, but it’s the most interesting pattern on the list.

## Self-Maintenance

If I ask you to "remember" something during a session:
- Add it to the appropriate section of this CLAUDE.md file
- Tell me what you added and where

If you notice a convention I'm using consistently that isn't in this file:
- Flag it at the end of the session and suggest how to document it

What you’ve built here is a file that learns. You make a decision mid-session — “actually, let’s always use zod for runtime validation from this point forward” — and instead of that preference evaporating when the session ends, it gets written into the file. Next session, it’s already there.

Claude also starts surfacing its own observations. You’ll get notes like: “I noticed you consistently name your custom hooks use[Resource]State — want me to add that to the conventions section?” You can ignore it or accept it. Either way, the option exists.

Over time the file gets more accurate, not less. That’s the opposite of what happens with documentation you maintain manually.

Global vs Local: Where the Rule Actually Belongs

One last thing that trips people up.

~/.claude/CLAUDE.md is for you — communication style, verbosity preference, output formatting, code standards you apply everywhere regardless of project. The stuff you’d have to re-explain to any tool, on any codebase, forever.

./CLAUDE.md (in the project root) is for the project — architecture rules, team conventions, file-specific behavior, external tool chains, project macros. The stuff that’s specific to this repo and this context.

If you’re working across six repos with six different stacks, the global file handles your personal preferences. Each project file handles what makes that project different from every other one you work on.

Start with the global. One good global CLAUDE.md immediately makes every project session better before you’ve written a single project-specific rule.

The honest version of this: most Claude Code power users have a CLAUDE.md that’s doing real work. Most casual users don’t. That gap shows up in output quality, review overhead, and how often they’re re-explaining things they’ve explained before.

Pick one workflow. Encode it. See what changes.

The full methodology for building out your CLAUDE.md as a persistent AI second brain — session design, memory architecture, the patterns that hold under real project pressure — is at [claude.md] Masterclass: Build Your Own Persistent AI Brain.

If you want the broader Claude Code playbook alongside it, Claude Code for Developers: 21 Productivity Tricks That Save Hours covers 21 techniques the official docs skip over — context management, tool chaining, multi-agent setup.

From Signup to Live Portfolio in Under 2 Minutes: A Walkthrough With Real Numbers

Most developer portfolios die in the drafting stage. You pick a template, start customizing, get stuck on copy or layout decisions, and the tab stays open for three weeks before you close it forever.

I wanted to see what actually happens when you remove that friction. So I tracked the numbers from getfolio.dev‘s first 24 signups.

The end result

19 live, published portfolios out of 24 total signups. That’s a 79% publish rate. No onboarding emails nudging people. No “complete your profile” reminders. Just a flow that’s short enough that most people finish it in one sitting.

5 of those 19 marked themselves as “open to work.” 16 connected their GitHub. The portfolios pull in repos, languages, contribution graphs, and star counts automatically from there.

How someone actually gets from zero to published

This is the real sequence, step by step.

1. Claim a username

83% of signups did this immediately. You pick a username, and that becomes your URL: getfolio.dev/yourname. No subdomain configuration, no DNS records at this stage. Just a name.

2. Connect GitHub

OAuth flow. Takes about 10 seconds. Once connected, getfolio pulls your public repos, languages, contribution activity, and stars. 63% of all signups reached this step, and from here almost nobody dropped off.

This is where the interesting part happens. Instead of manually listing projects and typing descriptions, your portfolio is already populated. Repos show up with their actual language breakdown and star counts. Your contribution graph appears. The data is live, so when you push code tomorrow, the portfolio reflects it.

3. Pick a theme and publish

Five themes right now. The split across published portfolios:

  • DarkPro: 37% (the most popular by a clear margin)
  • Terminal: 26%
  • Editorial: 21%
  • Glass: 16%
  • Minimal: 0% so far, which honestly surprised me

You pick one, optionally drag sections around, and hit publish. 95% of published portfolios also opted into the Explore directory, which makes them discoverable.

4. (Optional) Custom domain and analytics

Pro tier unlocks custom domains and privacy-first analytics. One person has upgraded so far out of 20 onboarded users. Zero churn. Custom domain usage is still at zero though, which tells me the default getfolio.dev/username URL is good enough for most people at this stage.

What’s actually working

The 75% end-to-end activation rate (signup to discoverable portfolio) is high for a tool with no onboarding flow beyond the product itself. I think the reason is simple: connecting GitHub does 80% of the work for you. There’s almost no blank-page problem.

The biggest drop happens between signup and GitHub connection (25% drop). Some of those accounts might be people who signed up to look around. Some might come back later. I don’t have session replay data, so I’m guessing.

What surprised me

Terminal theme being the second most popular. I built it partly as a novelty, figured maybe one or two people would use it. Five out of nineteen is more than I expected.

Also, nobody picked Minimal. That’s the cleanest, most traditional layout. Developers apparently want something with more personality than a white page with Helvetica.

If you want to try the same thing

The whole process:

  1. Sign up at getfolio.dev
  2. Pick a username
  3. Connect GitHub via OAuth
  4. Choose a theme (DarkPro if you want to go with the crowd)
  5. Rearrange sections if you want, or don’t
  6. Publish

The portfolio stays synced with your GitHub going forward. You don’t touch it again unless you want to.

837 views have come in across all portfolios so far, 542 unique. Small numbers, but for a product with 24 total users and no ad spend, it means the portfolios are getting seen.

What’s next

Blog sync with DEV.to and Hashnode is live. If you write articles, they show up on your portfolio automatically. I’m also watching whether the “open to work” badge (5 of 19 portfolios have it enabled) actually drives recruiter inbound. No data on that yet.

Originally published on getfolio.dev.

How to Choose an AI Gateway in 2026: The Checklist Engineers Actually Need

AI gateway model management interface showing multi-provider routing across AWS Bedrock, OpenAI, Anthropic, Groq, Vertex AI, and self-hosted models for enterprise AI infrastructure.

The AI gateway market in 2026 feels a lot like the API gateway market did years ago.

Suddenly everyone has one.

Every platform claims to support every model, every provider, every deployment style, every governance feature, every enterprise requirement… all at once.

And honestly, from the outside, a lot of them look identical.

That’s what makes evaluating AI gateways surprisingly difficult.

Most comparison articles don’t help either. They either turn into feature checklists with no real engineering context, or they read like vendor landing pages pretending to be educational content.

But once you actually start deploying AI systems in production, the decision becomes much less abstract.

The questions stop being:

“Does this support OpenAI?”

And start becoming:

“What happens when Anthropic goes down?”
“Can we trace a multi-agent workflow across 40 tool calls?”
“Can legal approve this deployment model?”
“Can we stop one team from burning the entire AI budget?”

That’s the real evaluation process.

And the biggest mistake teams make is choosing an AI gateway based on features before understanding their actual requirements.

Because in practice, the “best” AI gateway depends almost entirely on what kind of system you’re running.

Start With the Part Most Teams Ignore: Deployment Requirements

This is usually the first filter that should eliminate half your options immediately.

But most teams skip it and jump straight into feature comparisons.

That’s backwards.

Before evaluating routing, observability, or MCP support, you need to answer a much simpler question:

Where is your data allowed to go?

If the answer is “inside our own infrastructure only”, you can eliminate SaaS-only gateways immediately.

Because that single answer changes everything.

If your company has strict compliance or data residency requirements, SaaS-only gateways may already be disqualified before the evaluation even starts.

And this becomes increasingly common once AI systems start touching internal documents, customer data, support workflows, financial systems, or healthcare information.

A surprising number of “AI gateway” products still assume your traffic flows through vendor-managed infrastructure.

For some teams, that’s completely fine.

For others, it’s a hard no.

That’s why deployment flexibility matters more than most feature matrices suggest.

You should know upfront:

  • Do you need VPC deployment?
  • On-prem support?
  • Multi-cloud routing?
  • Air-gapped environments?
  • Regional isolation?
  • Private model hosting?

If those requirements exist, they’re not “advanced features.” They’re baseline constraints.

This is one reason platforms like TrueFoundry are getting attention in larger enterprise environments. The platform supports VPC, on-prem, air-gapped, and multi-cloud deployments while maintaining centralized governance across the stack.

It’s also compliant with SOC 2, HIPAA, GDPR, ITAR, and the EU AI Act, which becomes relevant very quickly once security and legal teams enter the conversation.

And realistically, they always do.

The 6 Capabilities That Actually Matter

This is where most AI gateway comparison articles become shallow.

They turn into giant feature tables:

✅ Supports multiple models
✅ Has logging
✅ Has rate limiting
✅ Has observability

But that doesn’t tell you whether the platform actually solves production problems.

The details matter more than the checkbox.

1. Multi-Model Routing and Fallback

Almost every gateway now claims to support multiple models.

That’s no longer impressive.

The real question is whether the platform can make intelligent decisions between them.

Because production traffic is messy.

Providers experience outages.
Latency spikes happen.
Costs fluctuate.
Different workloads need different models.

A useful gateway should let you define routing behavior based on actual business logic.

AI gateway model management interface showing multi-provider routing across AWS Bedrock, OpenAI, Anthropic, Groq, Vertex AI, and self-hosted models for enterprise AI infrastructure.Multi-provider AI gateway configuration showing centralized model management and routing across OpenAI, Anthropic, Bedrock, Vertex AI, and self-hosted models (source: TrueFoundry platform)

For example:

  • Route simple classification tasks to cheaper models
  • Route complex reasoning tasks to stronger models
  • Fail over automatically if a provider becomes unavailable
  • Shift traffic dynamically based on latency or cost

Without this, “multi-model support” is mostly cosmetic.

You’re still managing complexity manually.

And once multiple teams start deploying independently, manual routing becomes difficult to maintain very quickly.

2. Token-Level Cost Attribution

Most teams underestimate how fast AI costs become opaque.

At first, everything feels manageable.

Then three teams launch AI features simultaneously, multiple providers get introduced, and suddenly finance wants answers nobody can confidently give.

“Which team generated this spend?”
“Which models are driving costs?”
“Which applications are over budget?”

Basic request-level metrics don’t solve this.

You need token-level visibility tied to:

  • Teams
  • Users
  • Applications
  • Models
  • Workflows

And ideally, you need governance attached to that visibility.

Because dashboards alone don’t stop runaway spending.

Good AI gateways allow you to enforce:

  • Team-level budgets
  • Usage quotas
  • Rate limits
  • Spend caps
  • Routing rules based on cost thresholds

That’s the difference between monitoring AI usage and actually controlling it.

3. Guardrails on Both Inputs and Outputs

This is another area where marketing language gets fuzzy.

A lot of platforms advertise “AI safety” or “content filtering.”

But the important question is where those controls actually execute.

A production-grade gateway should inspect traffic in both directions.

Before the model sees the request:

  • Detect prompt injection attempts
  • Filter sensitive information
  • Enforce policy constraints
  • Validate structured inputs

And before the response reaches the application:

  • Detect data leakage
  • Block unsafe outputs
  • Apply compliance rules
  • Remove restricted information

That second layer matters more than many teams realize.

Because a surprising amount of risk appears in generated outputs, not just prompts.

Especially once agents start interacting with tools, documents, databases, and external systems.

4. MCP and Agent Support

This one is becoming impossible to ignore in 2026.

If a gateway only handles stateless inference requests, it’s already starting to feel incomplete.

Modern AI systems increasingly rely on:

  • MCP servers
  • Tool calling
  • Multi-step workflows
  • Stateful agents
  • Long-running sessions

And those introduce entirely different operational requirements.

The important question isn’t just:

“Does it support MCP?”

It’s:

“Was MCP designed into the architecture, or bolted on afterward?”

Because the difference shows up fast in production.

You start needing:

  • Tool-level permissions
  • Per-agent RBAC
  • Workflow tracing
  • Stateful session management
  • Governance across tool calls

A simple LLM proxy usually struggles here.

This is where unified platforms become more attractive, especially for teams building agentic systems instead of simple chat interfaces.

TrueFoundry approaches this by combining an AI Gateway, MCP Gateway, and Agent Gateway into a single control plane instead of treating them as disconnected systems.

Here’s what that unified architecture looks like in practice:

Unified AI Gateway, MCP Gateway, and Agent Gateway architecture running across AWS, Azure, GCP, on-prem, and air-gapped environments with routing, guardrails, governance, observability, and multi-model orchestration.Example of a unified AI infrastructure stack combining AI Gateway routing, MCP server governance, agent orchestration, observability, and multi-cloud deployment controls in a single control plane (Adapted from the TrueFoundry website)

That architecture becomes much more valuable once agents start interacting with enterprise tools at scale.

5. Observability Depth

Most gateways claim to offer observability.

But “observability” can mean anything from basic request logs to full distributed workflow tracing.

And those are not remotely the same thing.

The real test is this:

Can you trace a complete agent workflow from the original request through every model interaction and tool call?

Because debugging AI systems gets complicated very quickly.

Especially with:

  • Multi-agent systems
  • MCP tool chains
  • Retrieval pipelines
  • Long-running workflows
  • Human-in-the-loop steps

If an agent makes 40 tool calls before producing an output, you need visibility into the entire chain.

AI gateway observability dashboard showing LLM request metrics, MCP calls, guardrail activity, workflow tracing, error breakdowns, and token-level monitoring for production AI systems.Example of production-grade AI gateway observability showing request tracing, MCP activity, guardrail events, error analysis, and cost monitoring across agent workflows (source: TrueFoundry platform)

Not just the first request.

You should also check whether the gateway exports cleanly into your existing stack:

  • OpenTelemetry
  • Grafana
  • Datadog
  • Prometheus

If observability becomes siloed inside a proprietary UI, operations teams usually end up frustrated later.

6. Performance at Scale

This is where vague marketing claims become dangerous.

Latency matters more than most teams initially expect.

Especially for agent systems.

In multi-step agent workflows, even small gateway delays compound across dozens of sequential tool calls.

That’s why benchmarks matter.

Ask vendors directly:

  • What’s your p99 latency?
  • What throughput can a single instance handle?
  • What happens under failover conditions?
  • How does latency change with guardrails enabled?

And ask for real numbers, not adjectives.

For example, TrueFoundry handles 350+ RPS on a single vCPU with sub-3ms latency while processing 10B+ requests per month through its AI Gateway infrastructure.

Specific numbers are always more useful than phrases like “enterprise scale.”

The Questions You Should Ask Every Vendor

This is the part most comparison guides skip.

But honestly, these conversations usually reveal more than any feature page ever will.

Here are the questions I’d actually ask during an evaluation.

“Where does our data go?”

Ask them to show the architecture diagram.

Not the marketing diagram.

The real traffic flow.

You want to understand:

  • Whether requests pass through vendor infrastructure
  • What gets stored
  • What gets logged
  • What remains inside your environment

This single question eliminates a surprising number of options.

“What happens if your infrastructure goes down?”

A lot of AI gateways quietly become a central dependency.

Which means if the gateway fails, your entire AI stack fails with it.

You want to understand:

  • Failover behavior
  • Regional redundancy
  • Self-hosting options
  • Operational recovery paths

Especially if the platform is SaaS-first.

“Show me a full multi-agent workflow trace.”

Not a single request log.

A real workflow trace.

You want to see:

  • Tool calls
  • Routing decisions
  • Latency breakdowns
  • Guardrail events
  • Session context
  • Error propagation

If observability is weak during the demo, it usually becomes painful in production.

“Can you enforce per-agent RBAC?”

This matters more than people expect.

Team-level permissions aren’t enough once multiple agents start interacting with tools independently.

You need granular control.

Especially for:

  • MCP servers
  • Internal databases
  • Slack integrations
  • Financial systems
  • Sensitive documents

Otherwise, your blast radius expands very quickly.

“What MCP server integrations do you support out of the box?”

This matters more than it sounds.

A lot of gateways claim to support MCP now.

But there’s a big difference between:

“Supports MCP in theory”

and

“Actually integrates cleanly with the tools your teams already use.”

You want to understand how mature the ecosystem really is.

Ask them:

  • Which MCP servers are already supported?
  • How difficult is custom integration work?
  • Is tool discovery centralized?
  • Can integrations be governed with RBAC and guardrails?
  • Are MCP capabilities native to the architecture or added later as plugins?

Because once agents start interacting with internal systems at scale, MCP stops being a side feature.

This is where MCP support starts becoming operationally important instead of just theoretical:

MCP server management interface showing GitHub, Atlassian, Sentry, and Webflow integrations for enterprise AI agents with centralized governance and tool connectivity.Example of centralized MCP server management for AI agents, including GitHub, Atlassian, Sentry, and Webflow integrations with governance and authentication controls (source: TrueFoundry platform)

It becomes part of your operational infrastructure.

“What compliance certifications do you support?”

And more importantly:

“Can we see the reports?”

Because there’s a major difference between:
“Designed for compliance”
and
“Actually certified.”

That distinction matters to enterprise procurement teams immediately.

The Honest Trade-Offs

There’s no perfect option here.

Every approach comes with trade-offs.

And pretending otherwise usually makes technical content less trustworthy.

Lightweight open-source proxies

Tools like LiteLLM are excellent for getting started quickly.

They simplify model routing and reduce vendor lock-in.

But once governance, observability, and compliance requirements grow, teams often end up building additional infrastructure around them.

Eventually teams start rebuilding:

  • Observability
  • RBAC
  • Budget controls
  • Guardrails
  • Workflow tracing
  • Compliance layers

That overhead becomes real surprisingly fast.

SaaS AI gateways

These are usually the fastest to operate.

  • Minimal infrastructure overhead
  • Quick onboarding
  • Easy setup

But they may not satisfy:

  • Data residency requirements
  • Air-gap requirements
  • Regulated workloads
  • Internal security policies

Which means some enterprises hit architectural limits very early.

Unified enterprise platforms

This is where Kubernetes-native platforms like TrueFoundry fit.

The setup is more opinionated upfront because the platform combines:

  • AI Gateway
  • MCP Gateway
  • Agent Gateway
  • Governance
  • Observability
  • Deployment controls

Into one system.

That trade-off makes more sense for teams already operating Kubernetes environments, multi-cloud infrastructure, or agent-heavy workflows.

Especially once fragmented tooling starts becoming operationally expensive.

But smaller teams with lightweight workloads may genuinely not need that level of infrastructure yet.

And honestly, that’s fine.

A Simple Decision Tree

If you’re trying to narrow things down quickly, this is probably the simplest framework.

Small team + one model + no compliance requirements

Start simple.

Direct SDK access or a lightweight proxy is usually enough.

Avoid overengineering early.

Multiple teams + multiple models + basic governance needs

This is usually where a standalone AI Gateway starts making sense.

You need:

  • Centralized routing
  • Cost tracking
  • Rate limiting
  • Basic observability
  • Governance controls

Building agents that use tools

At this point, MCP support becomes mandatory.

You’re no longer managing simple inference traffic.

You’re managing workflows.

That changes the architecture significantly.

Multi-agent systems + compliance + data residency requirements

This is where unified platforms become much more compelling.

Especially if you need:

  • AI Gateway
  • MCP Gateway
  • Agent orchestration
  • Full observability
  • On-prem or VPC deployment
  • Centralized governance

In practice, this is the environment TrueFoundry is optimized for.

Final Thoughts

The AI gateway space is getting crowded very quickly.

And honestly, that’s probably a good sign. It means AI infrastructure is maturing.

But it also means feature lists are becoming less useful.

The better evaluation process starts with constraints:

  • Deployment requirements
  • Compliance needs
  • Team structure
  • Agent complexity
  • Operational maturity

Then works outward from there.

Because most teams don’t actually need “the most powerful AI gateway.”

They need the one that fits the system they’re realistically building over the next 12–24 months.

And those are very different decisions.

If you want to explore what a unified AI Gateway, MCP Gateway, and Agent Gateway stack looks like in practice, you can try TrueFoundry free, no credit card required, and deploy it in your own cloud in under 10 minutes.

Thanks for reading! 🙏🏻
I hope you found this useful ✅
Please react and follow for more 😍
Made with 💙 by Hadil Ben Abdallah
LinkedIn GitHub Twitter

The “Tutorial Hell” Trap: Stop Building Spotify Clones and Start Building Business Tools

I get a lot of DMs from new developers asking: “Is AI going to replace me?”

No. But I’ll tell you what will replace you: building things that no one pays for.

The new gen problem
Devs in 2026 are the best-educated generation ever. They can use AI to generate complex code in minutes.

But they don’t know how to answer one simple question:

“Who is going to pay for this, and why?”

Why “Spotify Clones” are a career killer

  • They take 3-6 months
  • They solve zero new problems
  • They teach you nothing about business constraints
  • They produce no clients, no revenue, no case studies

The better approach

  1. Pick a boring problem.

    • A local restaurant needs a waitlist.
    • A construction company needs a timesheet tracker.
    • A yoga studio needs a class booking system.
  2. Build the smallest possible version.

    • A single HTML page with a form.
    • A back-end script that writes to a Google Sheet.
    • A simple notification to the business owner.
  3. Charge for it.

    • $50/month for the first 3 months.
    • If they don’t pay, it wasn’t a real problem.

Code Example: The “Boring” MVP
`
— No fancy UI. No AI recommendations.
— Just a REST endpoint that inserts into a sheet.

from flask import Flask, request
import gspread

app = Flask(name)

@app.route(‘/submit’, methods=[‘POST’])
def submit_lead():
data = request.json
# Write to Google Sheet (owner can see it instantly)
gc = gspread.service_account()
sh = gc.open(“Lead Tracker”)
sh.sheet1.append_row([data[‘name’], data[’email’], data[‘message’]])
return {“status”: “ok”}

if name == ‘main‘:
app.run()
`

The outcome

  • 7 local clients paying 50/month=50/month=350/month passive
  • Real case study to show in interviews
  • Proof that you solve problems, not just write code

My full portfolio (built for real businesses):
Shahzaib

Case study (built with this approach):
Case Study

Build & Release #5: How Apple’s rsync Update Nuked My Repo

The sync script hadn’t changed. Same flags, same paths, same output format. I’d been running it every single time I pushed a change to the public repo, and it had always been rock solid.

Until April 8th. I ran the script, and the terminal output looked perfectly normal. But when I double-checked the local public repo directory, something was missing.

No .git folder. It was just… gone.

Why There’s a Script at All

A quick bit of context: 7onic lives a double life across two repositories. There’s a private repo where the full project lives — the docs site, design tokens, internal tooling, the whole nine yards. And then there’s the public repo (itonys/7onic) that contains only the open-source library: the core components, the CLI, and the token package.

To keep them in sync, I wrote a script that uses rsync to copy the relevant directories from private to public, filter out the internal fluff, and push the clean code.

Now, the .git folder in that public directory is what makes it a Git repository in the first place. It holds every commit, every branch, every tag, and most importantly, your remote configuration — the stuff that tells Git, “Hey, when I type git push, send this code to this exact GitHub URL.” If you delete that folder, you lose your entire local history. Fortunately, the remote on GitHub stays intact, so a quick git clone will save your skin. But any local-only state? Completely vaporized.

My script explicitly included --filter='P .git'. That P stands for Protect — a standard GNU rsync flag that tells the engine, “Whatever you do, do not touch anything named .git.” I had verified it. It had been working flawlessly for weeks.

What I didn’t know was that macOS had quietly pulled a fast one on me. A recent OS update had swapped out the native GNU rsync binary for openrsync, Apple’s own BSD-licensed re-implementation. And guess what? openrsync doesn’t give a damn about --filter='P .git' the same way GNU does.

Combined with the --delete-excluded flag — which ruthlessly purges files in the destination that match an exclude rule — the public repo’s .git folder was silently executed on the very next sync.

The Fix That Made It Worse

My immediate, panic-fueled instinct was: Okay, rsync needs to keep its hands off .git entirely.

The obvious move was to tweak the flags. But in my state of confusion over which flag was doing what, I made the classic mistake of changing two things at once: I ripped out the protect flag and kept the exclude.

Which meant rsync looked at the source directory — the private repo — and copied its .git folder right over to the public destination.

The public repo had a .git folder again, sure. But it was a clone of the private repo’s brain. This meant:

  • The public directory’s origin remote now pointed directly at the private GitHub repository.
  • Any commit made from inside the public directory would bypass the public repo and push straight to the private remote.

I didn’t realize this immediately. To verify my “fix” worked, I casually ran a test commit.

That commit flew straight into the private remote. I stared at the terminal git log for a long, quiet minute.

Oh, no.

321 Files Deleted. 93,300 Lines Gone.

I managed to untangle the origin mess first — wiped the imposter .git folder, ran a fresh git clone from GitHub to restore the actual public history, and got the public repo pointing back to itself.

But now, my local working tree was a crime scene. There was a bad commit (13edc14) sitting there from the whole ordeal that needed to be reconciled. I decided to merge it.

I did not run git diff --stat first. Big mistake.

The merge took the private repo’s file structure as the absolute source of truth and aggressively subtracted everything the public repo wasn’t supposed to have. Which, as it turned out, was almost the entire codebase.

321 files deleted. 93,300 lines of code wiped from existence.

site.config.ts got completely overwritten with the private version. package.json followed suit. What followed was a cascade of four consecutive errors. I fixed each one blindly, treating the symptoms without looking at the larger picture, only to trigger the next error. It was that specific kind of miserable debugging session where you’re chasing ghosts because you refuse to admit how deep the hole actually is.

The ultimate salvation was running git reset --hard f3453d8 — rolling back to the last clean commit before the madness started. It worked instantly and cleanly. The actual damage was luckily contained to my local working state and one embarrassing, stray commit on the private remote.

But it cost me four hours.

The Real Fix

The root problem was putting blind faith in rsync to protect a critical folder like .git. Any solution that relies on a specific CLI implementation behaving perfectly across different OS flavors is fundamentally fragile. GNU rsync and openrsync are simply not the same beast.

The new approach completely eliminates rsync from the equation of trust:

# Physically move .git completely out of rsync's line of sight
GIT_BACKUP="/tmp/.7onic-git-backup-$$"
mv "$DEST/.git" "$GIT_BACKUP"
trap 'mv "$GIT_BACKUP" "$DEST/.git" 2>/dev/null' EXIT

# rsync runs with .git physically absent from the destination
rsync -av --delete 
  --exclude='.git' 
  "$SRC/" "$DEST/"

# Restore it like nothing happened
mv "$GIT_BACKUP" "$DEST/.git"
trap - EXIT

It’s a three-layer defense: physically eject .git to /tmp before rsync even initializes so there’s nothing to delete, tell rsync to exclude it anyway just to be safe, and then slide it back into place afterward. The trap EXIT ensures that even if rsync crashes or the script aborts mid-run, the restore still fires. No flags to guess. No implementation quirks to worry about.

It hasn’t broken once since April 8th.

Post-Mortem

The merge was the ultimate unforced error. By the time I hit that step, I had already fixed the remote origin issue — I was technically in recovery mode, not still drowning. A single git diff --stat before hitting enter on that merge would have screamed “321 DELETIONS” and I would have stopped immediately.

I didn’t check because I was still trapped in “fix things fast” mode, which is historically the absolute worst mindset for carefully reviewing a git merge.

The initial script fix would have been fine too if I had just changed one thing at a time. Instead, I altered two flags simultaneously while my brain was fried, which is exactly how you end up copying a private .git directory somewhere it has absolutely no business being.

I’ve added one final line to the end of the sync script now: git -C "$DEST" remote -v. It prints the active origin remote after every single sync. It takes half a second. If it ever accidentally shows the private remote URL again, I’ll catch it instantly before a test commit can make a fool out of me.

Next: The same week I broke the sync script, I also reversed an npm install decision I’d documented in my own ADR — because the axios supply chain attack made my “just delete the lockfile” approach suddenly untenable.

About 7onic — An open-source React design system where design and code never drift. Free, MIT licensed. Docs and interactive playground at 7onic.design. Source code on GitHub — stars appreciated. More posts in this series at blog.7onic.design. Follow updates on X at @​7onicHQ.

The AI Workflow That Saved Me From a Debugging Spiral (And How to Replicate It)

Last quarter I lost a full afternoon to a bug that, in hindsight, took 11 minutes to fix once I changed how I was talking to my AI assistant.

Here’s what actually happened.

The Situation

We were mid-sprint. A new webhook integration kept silently dropping events under specific payload conditions — no exception, no log line, just gone. I’d been staring at the same three files for two hours. The code looked fine. The unit tests passed. My rubber duck was useless.

My first instinct, like most engineers who’ve been using AI tools for a while, was to paste the function into the chat and ask: “Why isn’t this working?”

I got back a confident, well-formatted, completely wrong answer. The model explained a race condition that didn’t exist in my codebase. I spent another 45 minutes chasing it.

The Problem With How I Was Using AI

The real issue wasn’t the tool. It was that I was treating it like a search engine with better grammar. I handed it a decontextualized code block and asked for a conclusion. No wonder it hallucinated one.

When you strip context and ask for answers, you get pattern-matched plausibility — not debugging insight. The model has no idea what should happen. It only knows what the code says.

The fix wasn’t a smarter model. It was a smarter prompt.

What I Tried (and What Actually Worked)

I stopped asking “what’s wrong” and started asking the model to reason out loud about expected vs. actual behavior — with me supplying both sides.

Here’s the exact prompt structure I switched to:

You are debugging with me, not for me.

Context:
- What this function is supposed to do: [one sentence]
- What it's actually doing: [one sentence]
- What I've already ruled out: [bullet list]

Here is the code:
[paste]

Do NOT guess at a root cause yet. First, ask me up to 3 clarifying questions that would most change your hypothesis.

That last line is the unlock. Instead of getting a confident wrong answer, I got three sharp questions — one of which was: “Does the payload schema validation run before or after the event is queued?”

I didn’t know. I checked. It ran after. That was the bug. A malformed payload was being queued before validation could reject it, and the queue consumer was silently swallowing the schema error.

Eleven minutes.

A Second Prompt That Now Lives in My Rotation

The debugging prompt fixed my immediate problem, but I noticed a broader pattern: I was also losing time during code reviews by catching things too late. So I built a pre-review prompt I run before I open a PR:

Review this code as a senior engineer who is skeptical but fair.

Focus only on:
1. Logic errors or edge cases I may have missed
2. Any place where my stated intent and the actual implementation diverge
3. One thing you'd change if this were your codebase

My intent with this code: [one sentence]

[paste code]

Do not comment on style, naming, or formatting.

The constraint at the end matters. Without it, you get four paragraphs about variable naming and one throwaway line about the actual logic bug.

What Changed After

I don’t use AI less now — I use it differently. The prompts are more structured. I treat the model like a sharp junior engineer who needs good questions more than they need free rein. I give it constraints, context, and a specific job to do.

The productivity gains got real when I stopped optimizing for getting answers and started optimizing for getting the right thinking surface. Sometimes that means asking the model to poke holes. Sometimes it means asking it to steelman a bad approach before I throw it out. Rarely does it mean just dumping code and waiting.

One More Thing

These patterns didn’t come together overnight. They came from a lot of failed prompts, a lot of confidently-wrong AI answers, and a lot of “wait, why did that work?” moments.

If any of this resonates, I put together a prompt playbook with the full set of patterns I now use across debugging, code review, and architecture decisions — you can grab it at https://gumroad.com/l/nhltvo.

OpenSRE: Build Your Own AI Incident-Investigation Agent

Most AI coding tools stop at the editor. They help you write code. But the hardest, most stressful part of running software is not writing it. It is the moment it breaks in production at 2 a.m.

OpenSRE is built for that moment.

The problem it solves

When an incident hits, the evidence is scattered. Logs are in Datadog. Metrics are in Grafana. The config change that caused it is in Git. Service dependencies live in your infra layer. Each system saw part of what happened. None of them saw all of it.

So you do it manually. You pull logs, line up timestamps, ping the colleague who knows that stack, and slowly piece the story together. It takes hours. Under on-call pressure, you often just ship a patch to get the system back up and figure out the real cause later.

OpenSRE automates that investigation.

What it is

OpenSRE is an open-source framework, built on LangGraph, for building AI-powered SRE agents that automate incident investigation and root cause analysis. It is Apache 2.0 licensed and maintained by Tracer.

The point is not a single fixed product. It is a toolkit. You plug in the alerting sources you already use and compose custom investigation workflows tailored to your own infrastructure.

How the investigation runs

When an alert fires, the agent works through a defined sequence:

  1. Ingest the alert from your monitoring or incident system.
  2. Assemble context from logs, metrics, configs, and dependencies.
  3. Frame failure modes the incident could plausibly be.
  4. Execute investigation queries across connected systems, in parallel.
  5. Evaluate hypotheses against the evidence collected.
  6. Deliver a root cause report and recommended next actions, to Slack out of the box.

The agent tests several hypotheses at once and stops when it has enough confidence to give a clear answer, rather than running forever or guessing early.

What it connects to

OpenSRE integrates with the systems that already power modern platforms:

  • Data platform: Apache Airflow, Kafka, Spark
  • Observability: Grafana, Datadog, CloudWatch, Sentry
  • Infrastructure: Kubernetes, AWS, GCP, Azure
  • Dev tools: GitHub
  • Communication: Slack, PagerDuty

Adding a new output destination, such as routing reports to PagerDuty or OpsGenie, is described as one of the easiest contributions you can make.

Design principles worth noting

OpenSRE leans on a few principles that matter for production use: deterministic investigations, evidence-backed conclusions, parallel hypothesis testing, and fully auditable workflows.

That last point is important. This is not a black-box LLM that hands you a guess. The investigation is traceable, so you can see why it reached a conclusion.

Getting started

You can try it without touching production. The repo ships a local Grafana plus Loki demo that produces a real root cause report with one command:

git clone https://github.com/Tracer-Cloud/open-sre-agent
cd open-sre-agent
make install
make install-hooks
cp .env.example .env
opensre onboard
make local-grafana-live

The opensre onboard step walks you through configuring a local LLM provider and optionally validating integrations like Grafana, Datadog, Slack, AWS, GitHub, and Sentry. There is also a bundled demo that skips Docker entirely if you just want to see the flow.

Is it useful?

Promising, with caveats worth being honest about.

It is the youngest of the new wave of AI-agent tooling, with a smaller community and no tagged releases yet. It is also clearly aimed at data-platform teams, the Airflow, Kafka, and Spark crowd. If that describes your stack and on-call is genuinely painful, the local demo is worth an afternoon.

Heed the project’s own security guidance: use read-only credentials, restrict network exposure, log every investigation, and always review a report before any automated remediation. An agent touching production systems deserves that caution.

The takeaway

AI agents are moving past the editor and into operations. OpenSRE is an early, open look at what an AI SRE actually involves: not a magic fix-it button, but a structured, auditable investigator that correlates the signals you already have. If incident response on your team still means hours of manual log-correlation, it is a project worth watching and, if your stack fits, worth trying.