DEV Community

Anuroop Saxena
Anuroop Saxena

Posted on

How I Stopped Repeating Architectural Mistakes because of a Greek Goddess

When I started my new software engineering internship, I was handed a codebase that was, surprisingly, in decent shape. The code was relatively clean, the CI/CD pipelines ran green, and the test coverage was passable. But after my first week, I ran into a wall: the context was entirely missing.

I needed to make a minor change to how we processed streaming data. I noticed we were using a slightly unusual polling mechanism instead of a dedicated queue like Kafka. I asked the senior engineers I was supposed to report to, but the response was essentially a shrug. The original authors had left the company several months ago, the pull requests were titled "Fix data pipeline", and the Slack conversations where the actual decisions happened were lost to the 90-day retention limit.

The team was suffering from acute engineering amnesia. The code told me what the system did, but absolutely nothing about why it was built that way.

I remembered reading about Mnemosyne, the Greek goddess of memory, and decided that if humans couldn't remember why we built things, I needed to build a system that would. I started working on Mnemo—an engineering déjà vu and pre-mortem agent designed to permanently index the reasoning behind technical decisions and inject them directly into our daily workflow.

Here is how I built it, the technical hurdles of scraping context, and why passive indexing is the only way to stop your team from repeating past architectural mistakes.

The Core Problem: Documentation Rots

The standard answer to engineering amnesia is "write more design docs." But as any experienced engineer knows, documentation rots the second it is merged. If a developer has to leave their IDE to write a Notion page or update a company wiki, it simply won't happen. The incentive structure of software development rewards shipping features, not chronicling history.

I needed a system that passively watched our primary communication channels—GitHub and chat applications—and extracted the underlying architectural intent. But taking unstructured Slack messages and massive schema.prisma files and making them searchable is difficult. Keyword search is useless when you search for "database choice" and the original discussion only mentions "Postgres constraints" or "Prisma migration limits."

I needed semantic memory. Specifically, I needed a way to store this context so an LLM agent could retrieve and synthesize it later based on meaning, rather than exact text matches.

This is where I integrated Hindsight, an open-source tool built explicitly for this kind of problem. Instead of standing up my own Pinecone cluster, manually generating OpenAI embeddings, and wrestling with LangChain abstractions, Hindsight gave me a clean API to push text and metadata, handling the embedding, chunking, and vector retrieval under the hood. You can read more about the mechanics in the Hindsight docs.

Ingesting the Codebase

The first step was to seed Mnemo with the context we already had. I set up GitHub webhooks to listen for changes to core infrastructure files. Whenever a package.json or schema.prisma was modified, Mnemo would intercept the event, extract the diff, and ingest it into the memory bank.

The implementation is surprisingly straightforward. In our Next.js backend, the webhook handler filters for structural files and uses the Hindsight client to retain the context.

import { HindsightClient } from '@vectorize-io/hindsight-client'

const client = new HindsightClient({
  apiKey: process.env.HINDSIGHT_API_KEY,
  baseUrl: process.env.HINDSIGHT_BASE_URL,
})

export async function retain(bankId: string, content: string, metadata: any = {}) {
  // Push the unstructured context into the vector index
  return await client.retain(bankId, content, metadata)
}
Enter fullscreen mode Exit fullscreen mode

In the webhook route, we specifically target architectural files. We aren't indexing every single typo fix in a CSS file or React component; we want to know when the core data model shifts or when a new heavy dependency is introduced.

// Inside app/api/webhooks/github/route.ts
if (file.filename === 'schema.prisma') {
  await retain(
    workspace.hindsightBankId, 
    `Repository Prisma Schema (Database Architecture) for ${fullName}\n\n${fileContent.slice(0, 5000)}`,
    { type: 'inferred_architecture', source: 'github_webhook' }
  )
}
Enter fullscreen mode Exit fullscreen mode

By passively indexing these files, Mnemo builds a baseline understanding of the application's structure. But the real value comes from capturing the human context—the debates, the trade-offs, and the compromises.

Building the Pre-Mortem Agent

Having a searchable database of past decisions is nice, but it requires developers to actively go and search for it. In my experience, developers rarely stop to search a knowledge base before writing code. I wanted Mnemo to act as a "pre-mortem" agent. If a developer opens a Pull Request proposing to reintroduce Redis for caching, Mnemo should automatically chime in and say, "Wait, we removed Redis six months ago because of memory leak issues."

To do this, I needed robust Vectorize agent memory capabilities. When a PR is opened, Mnemo takes the description and the diff, and queries Hindsight for semantically similar past decisions.

// Inside app/api/memory/premortem/route.ts
export async function runPreMortem(text: string, workspaceId: string) {
  // 1. Recall similar historical decisions from Hindsight
  const memories = await client.recall(workspaceId, text, 5)

  if (!memories.length) return null;

  // 2. Synthesize a warning if we are repeating a mistake
  const prompt = `Analyze these past decisions: ${JSON.stringify(memories)}. 
  Does the proposed change: "${text}" conflict with or repeat a past failure?`

  return await generateLLMResponse(prompt);
}
Enter fullscreen mode Exit fullscreen mode

This fundamentally changed how we operate. The context is surfaced before the code is merged, turning a post-mortem into a pre-mortem. Instead of discovering an architectural bottleneck in production, the developer is warned in the GitHub comments while the code is still in review.

The Discord Integration: Fighting Serverless Cold Starts

To make Mnemo truly frictionless, it had to live where the engineers communicate. For us, that meant building a Discord bot natively integrated into our engineering channels. Developers could type /why did we drop Kafka? directly in chat, and Mnemo would query Hindsight and provide a cited answer immediately. They could also explicitly save decisions using a /remember slash command during architecture discussions.

However, deploying a Discord interaction bot on a serverless platform (we used Vercel) introduced a painful, platform-specific edge case. Discord strictly requires your bot to acknowledge an interaction within exactly 3.0 seconds, or it terminates the request and throws an InteractionFailed error to the user. Vercel cold starts, combined with the latency of establishing a connection and querying a vector database, frequently took 4 to 6 seconds.

Our bot was caught in a continuous crash loop during periods of low activity. Users would type a command, the serverless function would spin up, breach the 3-second timeout, and crash.

To solve this, I had to implement a highly defensive Promise.race architecture. If the background process handling the Hindsight query takes longer than 2.0 seconds, we immediately defer the reply to satisfy Discord's strict timeout constraints, while the heavy lifting continues in the background.

// Inside discord-bot/index.ts
const timeoutPromise = new Promise((resolve) => 
  setTimeout(() => resolve('TIMEOUT'), 2000)
)

// Race the actual query against the 2-second timeout
const result = await Promise.race([
  processMnemoQuery(interaction),
  timeoutPromise
])

if (result === 'TIMEOUT') {
  // Satisfy Discord's 3-second rule immediately
  await interaction.deferReply({ ephemeral: false })
  await interaction.editReply('⚠️ Querying Hindsight memory... (cold start)')
}
Enter fullscreen mode Exit fullscreen mode

This fallback pattern feels slightly hacky, but it is an absolute necessity if you are running interactive chat bots on serverless infrastructure. Once the timeout is successfully mitigated, the bot safely edits the initial message with the fully synthesized response. It ensures the bot never appears offline, even when the container is waking up from a cold boot.

Results and Real-World Usage

The impact of having a centralized, queryable engineering memory was immediate and profound.

Last week, a newer engineer was tasked with migrating a subset of our internal API to a new routing structure. In the PR description, they mentioned pulling in a specific, heavy validation library. Mnemo's pre-mortem webhook fired, queried Hindsight, and immediately posted an automated comment on the PR:

"Warning: We explicitly removed this validation library in PR #402 because it introduced a massive bundle size regression. Consider using our internal validation utility instead."

That one automated comment saved hours of code review, back-and-forth discussions, and potential performance debugging.

Furthermore, the Discord bot has become our de facto onboarding tool. Instead of asking a senior engineer to drop what they are doing and explain the data pipeline for the fifth time, a new hire can simply ask Mnemo. Mnemo pulls the original chat threads where the pipeline was designed, summarizes the constraints, and links directly to the exact pull requests where the initial code was implemented.

Lessons Learned

Building Mnemo forced me to confront a few harsh truths about how software engineering teams actually operate, versus how we wish they operated. Here are my main takeaways from building an automated memory agent:

1. Documentation must be passive.
If your strategy for retaining context relies on engineers proactively writing wiki articles after shipping a feature, your strategy will fail. Context must be scraped passively from where engineers are already talking (Slack, Discord, PR descriptions) and automatically indexed. If it requires a context switch, it won't happen.

2. Vector search is non-negotiable for architectural intent.
You cannot grep for intent. When querying past decisions, the vocabulary used to describe a problem often changes drastically over time. Storing raw text in a Postgres database and using standard full-text search simply does not work for conceptual architecture questions. Leveraging a dedicated vector memory engine was the only way to surface relevant context accurately regardless of the exact phrasing.

3. Serverless bots require aggressive defensive programming.
Platform constraints like Discord's 3-second interaction window will absolutely break your bot if you deploy to a serverless environment with cold starts. You must architect your handlers to fail fast or defer immediately. Do not trust your local development response times; they lie to you.

4. Engineering amnesia is a tooling problem, not a culture problem.
We often blame teams for "not communicating well," "siloing knowledge," or "moving too fast." The reality is that our tools are designed for transient communication. By treating technical context as a first-class citizen and persisting it into long-term memory, you stop blaming the team and start fixing the infrastructure.

If you find yourself answering the same architectural questions repeatedly, or staring at a schema.prisma file wondering why a particular table or relation exists, it might be time to start indexing your decisions. Human memory is fragile, but infrastructure doesn't have to be.

Top comments (0)