<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Coherence</title>
    <description>The latest articles on Forem by Coherence (@coherence_ai).</description>
    <link>https://forem.com/coherence_ai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F12779%2F75c8e1bd-a31f-4834-a0f3-ceff84324f15.png</url>
      <title>Forem: Coherence</title>
      <link>https://forem.com/coherence_ai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/coherence_ai"/>
    <language>en</language>
    <item>
      <title>AI-Native CRM vs. Legacy CRM: The Architecture Decision That Determines Your Sales Team's Future</title>
      <dc:creator>Keith Fawcett</dc:creator>
      <pubDate>Mon, 30 Mar 2026 17:46:14 +0000</pubDate>
      <link>https://forem.com/coherence_ai/ai-native-crm-vs-legacy-crm-the-architecture-decision-that-determines-your-sales-teams-future-2bm6</link>
      <guid>https://forem.com/coherence_ai/ai-native-crm-vs-legacy-crm-the-architecture-decision-that-determines-your-sales-teams-future-2bm6</guid>
      <description>&lt;p&gt;&lt;em&gt;A technical and strategic breakdown for founders, RevOps leaders, and developers building lean B2B sales operations.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;If you're evaluating CRM platforms in 2026, you've noticed that every vendor claims to be "AI-powered." Salesforce has Einstein. HubSpot has Breeze. Microsoft Dynamics has Copilot. The marketing language has converged completely — but the architectures behind the marketing language have not.&lt;/p&gt;

&lt;p&gt;This post makes a specific, testable argument: &lt;strong&gt;there is a fundamental architectural difference between AI-native CRM and AI-augmented CRM&lt;/strong&gt;, and that difference determines whether your AI capabilities compound over time or remain static features you pay a premium to access.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem with "AI-Powered" CRM Marketing
&lt;/h2&gt;

&lt;p&gt;The phrase "AI-powered" has become meaningless as a differentiator. Consider what it actually covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A rule-based email suggestion engine: "AI-powered"&lt;/li&gt;
&lt;li&gt;A GPT-4 wrapper that drafts follow-up emails: "AI-powered"&lt;/li&gt;
&lt;li&gt;A fully autonomous agent that manages your entire prospecting workflow end-to-end: "AI-powered"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not the same thing. They differ by orders of magnitude in architectural sophistication, data requirements, and business impact. The number that matters: sellers who effectively partner with AI tools are &lt;strong&gt;3.7× more likely to meet quota&lt;/strong&gt; than those who don't (Gartner, 2025). But that figure assumes genuinely effective AI partnership — not a chatbot inside your CRM.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Five Architectural Pillars of AI-Native CRM
&lt;/h2&gt;

&lt;p&gt;Based on analysis of current market leaders and purpose-built AI-native platforms, here are the five pillars that separate genuine AI-native architecture from AI-augmented legacy systems:&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 1: Autonomous Data Enrichment
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Legacy CRM:&lt;/strong&gt; Data quality is a human responsibility. Records are as good as the last time someone manually updated them. 37% of CRM users report revenue loss due to poor data quality (Teamgate, 2025).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-native CRM:&lt;/strong&gt; The system continuously enriches contact, company, and deal records without manual intervention — pulling from email signals, web activity, firmographic databases, and behavioral patterns. Data quality is an automated, continuous process, not a periodic cleanup project.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why it matters:&lt;/em&gt; AI is only as good as its data. Autonomous enrichment creates a self-improving data foundation that makes every downstream AI capability more accurate over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 2: Proactive Intelligence
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Legacy CRM:&lt;/strong&gt; The system surfaces what you ask for. You run a report, you get a report. Insight generation is human-initiated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-native CRM:&lt;/strong&gt; The system surfaces what you need before you ask. Deals at risk. Contacts who haven't been touched. Prospects signaling buying intent. Follow-ups that have been missed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why it matters:&lt;/em&gt; The value of intelligence is perishable. By the time a human reviews reports and identifies an at-risk deal, the opportunity to intervene may have passed. Proactive intelligence captures value in the moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 3: Natural Language Interaction
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Legacy CRM:&lt;/strong&gt; Interaction is through forms, dropdowns, and predefined fields. Adding a note, updating a deal stage, or running a report requires navigating UI designed around data entry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-native CRM:&lt;/strong&gt; Users interact through natural language. "Show me all deals over $50k that haven't had activity in 14 days and draft a follow-up for each." One sentence. Executed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why it matters:&lt;/em&gt; Natural language interaction removes the CRM adoption barrier. The tool works for users rather than requiring users to learn how to work it. This directly addresses the 75% administrative burden problem (Bain &amp;amp; Company, 2025).&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 4: Agentic Workflow Execution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Legacy CRM:&lt;/strong&gt; Automation is rule-based. When X happens, do Y. The rules are predefined by humans and cannot navigate ambiguity or exception cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-native CRM:&lt;/strong&gt; AI agents pursue goals across multi-step workflows, making contextual decisions at each step. A prospecting agent doesn't just send a template — it researches the prospect, selects the most relevant value proposition, drafts personalized outreach, adjusts based on response patterns, and escalates to human review when confidence is low.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why it matters:&lt;/em&gt; Gartner (2025) predicts 60% of B2B sales workflows will be partly or fully automated through AI by 2028, up from 5% in 2023. The AI agent market is growing at a 45% CAGR (BCG, 2025). Agentic capacity is not a future capability — it is a current competitive differentiator.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pillar 5: Adaptive Learning
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Legacy CRM:&lt;/strong&gt; AI features operate on static or periodically updated models. The lead scoring model you configured 12 months ago may not reflect current market dynamics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-native CRM:&lt;/strong&gt; The system continuously learns from outcomes. Every won deal, lost deal, responded email, and ignored follow-up updates the model. The AI becomes more accurate and more valuable as the business processes more data through it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why it matters:&lt;/em&gt; This is the compounding returns dynamic. Year 1 ROI and Year 3 ROI from AI-native CRM are categorically different, because the Year 3 model has processed thousands more data points from your specific customer base. CRM implementations with effective AI achieve ROI of up to 245% (Teamgate, 2025) — compared to the baseline $8.71 per $1 invested for standard CRM.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Data Quality Crisis in Legacy Systems
&lt;/h2&gt;

&lt;p&gt;Before any AI can operate effectively, the underlying data must be reliable. This is where legacy CRM systems face their most fundamental challenge.&lt;/p&gt;

&lt;p&gt;Legacy systems rely on humans to maintain data quality. Humans are inconsistent. They're incentivized to sell, not to update records. They forget. They enter data in inconsistent formats. The result: &lt;strong&gt;37% of CRM users report revenue loss due to poor data quality&lt;/strong&gt; (Teamgate, 2025).&lt;/p&gt;

&lt;p&gt;AI-native CRM addresses this at the architectural level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bidirectional email sync captures every interaction automatically&lt;/li&gt;
&lt;li&gt;Contact enrichment pulls from multiple data sources continuously&lt;/li&gt;
&lt;li&gt;Behavioral signal tracking updates records based on actual activity&lt;/li&gt;
&lt;li&gt;Duplicate detection and data validation run continuously&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a self-healing data layer — one that enables AI capabilities to compound in accuracy rather than degrade under the weight of stale data.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Economics of AI-Native vs. Legacy for Lean Teams
&lt;/h2&gt;

&lt;p&gt;The cost structure of legacy CRM platforms was designed for enterprise economics. HubSpot's AI-enhanced Sales Hub starts at $90/user/month. Salesforce Einstein ranges from $75-$300/user/month for AI features on top of base platform costs. For a 5-person team, you're looking at $450-$1,500/month minimum before professional services and implementation.&lt;/p&gt;

&lt;p&gt;More importantly: the AI capabilities in these platforms are often gated behind premium tiers. The AI features that matter most — autonomous workflow execution, predictive scoring, advanced personalization — are not available in entry-level tiers. You pay enterprise prices for enterprise-designed tools that weren't built for the way lean teams actually work.&lt;/p&gt;

&lt;p&gt;AI-native platforms built for lean teams invert this model. AI is not a premium add-on — it is the default operating layer, available at every tier. The economics reflect lean-team unit costs, not enterprise overhead. And the architecture reflects the reality of a 3-person team, not a 300-person sales org.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Technical Evaluation Checklist
&lt;/h2&gt;

&lt;p&gt;If you're evaluating CRM platforms and want to test whether a vendor's "AI-native" claims are real, ask these specific questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data layer:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Does email sync work bidirectionally across all tiers, or only in premium tiers?&lt;/li&gt;
&lt;li&gt;[ ] How is contact enrichment performed — manual, batch-scheduled, or continuous?&lt;/li&gt;
&lt;li&gt;[ ] How does the system detect and resolve duplicate records?&lt;/li&gt;
&lt;li&gt;[ ] What is the data freshness guarantee for contact and company records?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI execution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Can AI agents execute multi-step workflows without human initiation of each step?&lt;/li&gt;
&lt;li&gt;[ ] What is the scope of autonomous actions available — read-only, draft, or full execution?&lt;/li&gt;
&lt;li&gt;[ ] How does the system handle edge cases that fall outside predefined workflow paths?&lt;/li&gt;
&lt;li&gt;[ ] What human oversight controls govern autonomous AI execution?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Learning and improvement:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] How often is the lead scoring model updated, and what data drives those updates?&lt;/li&gt;
&lt;li&gt;[ ] Can you access model performance data to understand how AI recommendations are performing?&lt;/li&gt;
&lt;li&gt;[ ] Does the system learn from your specific business outcomes, or from a generic model?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Integration architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Is email integration native (OAuth/IMAP sync) or third-party dependent?&lt;/li&gt;
&lt;li&gt;[ ] What is the latency between a real-world event and CRM record update?&lt;/li&gt;
&lt;li&gt;[ ] Can AI access full conversation context from email and calendar, or only logged activities?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cost structure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] What is the total cost to enable all AI capabilities for a 5-person team?&lt;/li&gt;
&lt;li&gt;[ ] Which AI features require usage-based fees beyond the subscription?&lt;/li&gt;
&lt;li&gt;[ ] What professional services are typically required for AI feature activation?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Compounding Advantage: Why Switching Costs Increase Over Time
&lt;/h2&gt;

&lt;p&gt;Here's the competitive dynamic that makes the AI-native vs. legacy decision more consequential than typical software choices: &lt;strong&gt;the value of AI-native CRM compounds over time, while legacy CRM value plateaus&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A legacy CRM's core value — storing and organizing customer data — does not meaningfully improve in Year 3 compared to Year 1. The data gets more complete, but the intelligence layer stays relatively static.&lt;/p&gt;

&lt;p&gt;An AI-native CRM's value in Year 3 is categorically higher than Year 1, because the AI has spent three years learning from your specific customer base — which deals convert, which messaging resonates, which follow-up timing works, which customer profiles expand. This institutional intelligence is non-transferable. If you switch platforms after three years, you restart the learning curve from scratch.&lt;/p&gt;

&lt;p&gt;The implication: every month you delay transitioning to AI-native CRM is not just a month of foregone efficiency — it is a month of compounding institutional intelligence that your AI-native competitors are building and you are not.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;The architectural decision you make about CRM is not a feature choice — it is a strategic choice about whether your revenue operations will compound in intelligence over time or remain static.&lt;/p&gt;

&lt;p&gt;The data is clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;81% of sales teams&lt;/strong&gt; are now using AI (Salesforce, 2024)&lt;/li&gt;
&lt;li&gt;AI users are &lt;strong&gt;3.7× more likely to hit quota&lt;/strong&gt; (Gartner, 2025)&lt;/li&gt;
&lt;li&gt;AI-native CRM delivers up to &lt;strong&gt;245% ROI&lt;/strong&gt; vs. $8.71 baseline for standard CRM (Teamgate, 2025)&lt;/li&gt;
&lt;li&gt;The AI in CRM market will grow from &lt;strong&gt;$11B to $48.4B by 2033&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The question is not whether AI will run your revenue operations. It's whether you'll build that capability on a foundation designed for it, or retrofit it onto infrastructure designed for a different era.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built at &lt;a href="https://getcoherence.io" rel="noopener noreferrer"&gt;Coherence&lt;/a&gt; — the AI-native XRM for founders and lean B2B teams. 600+ integrations, true email sync, autonomous AI agents, starting free.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt; Bain &amp;amp; Company (2025), BCG (2025), Gartner (2025), HubSpot (2024), McKinsey (2024), Salesforce (2024/2025), SellersCommerce (2025), Sopro (2025), Teamgate (2025).&lt;/p&gt;

</description>
      <category>crm</category>
      <category>ai</category>
      <category>sales</category>
      <category>b2bsales</category>
    </item>
    <item>
      <title>Building a Developer-First CRM: Why Your API Should Be the Product</title>
      <dc:creator>Keith Fawcett</dc:creator>
      <pubDate>Sun, 29 Mar 2026 10:00:56 +0000</pubDate>
      <link>https://forem.com/coherence_ai/building-a-developer-first-crm-why-your-api-should-be-the-product-3dni</link>
      <guid>https://forem.com/coherence_ai/building-a-developer-first-crm-why-your-api-should-be-the-product-3dni</guid>
      <description>&lt;h1&gt;
  
  
  Building a Developer-First CRM: Why Your API Should Be the Product
&lt;/h1&gt;

&lt;p&gt;When most CRM vendors talk about "API access," they mean an afterthought—a way to export data or sync contacts on a schedule.&lt;/p&gt;

&lt;p&gt;That's not an API. That's a data pump.&lt;/p&gt;

&lt;p&gt;A developer-first CRM treats the API as a first-class citizen. Here's what that actually means for the 28.7 million developers worldwide building, buying, or recommending business software.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with "Enterprise API" Thinking
&lt;/h2&gt;

&lt;p&gt;Traditional CRM APIs are built for enterprise integration scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long-running sync jobs that run nightly&lt;/li&gt;
&lt;li&gt;Bulk operations designed for millions of records&lt;/li&gt;
&lt;li&gt;Authentication that requires a PhD to configure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For solo developers and small teams? These APIs are like bringing a forklift to move a box.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Developer-First Actually Looks Like
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Webhooks as the default&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of "poll our API every 5 minutes to check for new leads," think: "we'll tell you when something happens."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deal.stage_changed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-27T09:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deal_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deal_abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"from_stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"qualified"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"to_stage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proposal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. REST that actually RESTs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CRUD operations that feel natural:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /contacts&lt;/code&gt; to create&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /contacts/{id}&lt;/code&gt; to retrieve&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PATCH /contacts/{id}&lt;/code&gt; to update&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE /contacts/{id}&lt;/code&gt; to... you get it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Rate limits that accommodate real automation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your automation runs every 5 minutes and hits rate limits, it's not an automation. It's a schedule you're fighting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Error messages that help&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ "Rate limit exceeded"
✅ "Rate limit exceeded (1000/hour). Retry after 3 seconds or implement exponential backoff."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Solo Developer Advantage
&lt;/h2&gt;

&lt;p&gt;Here's the thing: solo developers have the same needs as enterprise teams—just without the bureaucracy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sync with email (Gmail, Outlook)&lt;/li&gt;
&lt;li&gt;Calendar integration (Google Calendar, Cal.com)&lt;/li&gt;
&lt;li&gt;Form submissions (Typeform, JotForm)&lt;/li&gt;
&lt;li&gt;Payment tracking (Stripe)&lt;/li&gt;
&lt;li&gt;Support ticketing (Intercom, Zendesk)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference? Solo developers need this to work on day one, not after a 6-week implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Test: 30-Minute Integration
&lt;/h2&gt;

&lt;p&gt;The best test for a developer-first CRM:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect to your email tool&lt;/li&gt;
&lt;li&gt;Connect to your calendar&lt;/li&gt;
&lt;li&gt;Connect to one form tool&lt;/li&gt;
&lt;li&gt;Verify data flows automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can't do this in 30 minutes, it's not developer-first. It's developer-tolerant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for CRM Buyers
&lt;/h2&gt;

&lt;p&gt;If you're evaluating CRMs and the vendor can't explain their API in under 2 minutes, that's a red flag.&lt;/p&gt;

&lt;p&gt;If their docs require you to contact "sales engineering" to get API access, that's another red flag.&lt;/p&gt;

&lt;p&gt;A developer-first CRM should feel like using a well-designed API—not like requesting permission to use one.&lt;/p&gt;

&lt;p&gt;What developer-first features would you actually use? Reply in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is part of a series on building tools for solo founders. Follow for more insights on developer experience and CRM architecture.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How We Cut Our Deploy Time from 60 Minutes to 15 Minutes</title>
      <dc:creator>Keith Fawcett</dc:creator>
      <pubDate>Mon, 23 Mar 2026 09:42:20 +0000</pubDate>
      <link>https://forem.com/coherence_ai/how-we-cut-our-deploy-time-from-60-minutes-to-15-minutes-451p</link>
      <guid>https://forem.com/coherence_ai/how-we-cut-our-deploy-time-from-60-minutes-to-15-minutes-451p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A practical walkthrough of how we optimized our CI/CD pipeline for a 12-service monorepo on DigitalOcean App Platform — from pre-built Docker images to parallel CI jobs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem: An Hour Per Deploy
&lt;/h2&gt;

&lt;p&gt;Our platform is a TypeScript monorepo with 12 backend microservices, a React SPA, shared packages, and a PostgreSQL database. We deploy to DigitalOcean App Platform. Until last week, every push to &lt;code&gt;main&lt;/code&gt; took roughly 60 minutes to reach production.&lt;/p&gt;

&lt;p&gt;For a small team shipping fast, that is unacceptable. A one-hour deploy loop means you either batch changes (risky) or spend your afternoon watching progress bars. We decided to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Time Was Going
&lt;/h2&gt;

&lt;p&gt;Before optimizing, our pipeline had three sequential phases:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Duration&lt;/th&gt;
&lt;th&gt;What It Did&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CI checks&lt;/td&gt;
&lt;td&gt;~20 min&lt;/td&gt;
&lt;td&gt;Install deps, build monorepo, lint, typecheck, test, security audit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;~15 min&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Duplicate&lt;/strong&gt; install + build, then RLS coverage check and dep audit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DO deploy&lt;/td&gt;
&lt;td&gt;~25-35 min&lt;/td&gt;
&lt;td&gt;DigitalOcean builds 12 Docker images from scratch, deploys everything&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The biggest offender was the DigitalOcean deploy phase. App Platform clones the repo and builds every Dockerfile independently — with no Docker layer cache between deployments. Twelve services, each running &lt;code&gt;pnpm install&lt;/code&gt; and &lt;code&gt;tsc&lt;/code&gt; from scratch, every single time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 1: Pre-Build Docker Images in GitHub Actions
&lt;/h2&gt;

&lt;p&gt;This was the single biggest win. Instead of letting DigitalOcean build 12 Dockerfiles from scratch, we now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build all 12 service images &lt;strong&gt;in parallel&lt;/strong&gt; using a GitHub Actions matrix&lt;/li&gt;
&lt;li&gt;Push them to DigitalOcean Container Registry (DOCR)&lt;/li&gt;
&lt;li&gt;Update the live app spec to point at the pre-built images&lt;/li&gt;
&lt;li&gt;Tell DO to deploy — it just pulls the images, no building&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key enabler is &lt;code&gt;docker/build-push-action&lt;/code&gt; with GitHub Actions cache:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build-images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;fail-fast&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auth-service&lt;/span&gt;
          &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;services/auth/Dockerfile&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;module-config-service&lt;/span&gt;
          &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;services/module-config/Dockerfile&lt;/span&gt;
        &lt;span class="c1"&gt;# ... 10 more services&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.digitalocean.com&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v6&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
        &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.dockerfile }}&lt;/span&gt;
        &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.digitalocean.com/coherence/${{ matrix.name }}:${{ github.sha }}&lt;/span&gt;
        &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,scope=${{ matrix.name }}&lt;/span&gt;
        &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max,scope=${{ matrix.name }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;type=gha&lt;/code&gt; cache stores Docker layers in GitHub Actions cache. First build is cold (~8 min per service), but subsequent builds with unchanged layers take &lt;strong&gt;2-3 minutes&lt;/strong&gt;. Since all 12 run in parallel, the wall clock time is just the slowest individual build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result: DO deploy dropped from ~30 min to ~7 min.&lt;/strong&gt; It just pulls images and runs health checks — no building.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Spec Update Trick
&lt;/h3&gt;

&lt;p&gt;DigitalOcean App Platform specs can reference either a GitHub repo (source build) or a DOCR image (pre-built). We use &lt;code&gt;yq&lt;/code&gt; to dynamically update the live spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Fetch the live spec (preserves all encrypted secrets)&lt;/span&gt;
doctl apps spec get &lt;span class="nv"&gt;$APP_ID&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/spec.yaml

&lt;span class="c"&gt;# Swap each service from GitHub source to DOCR image&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;svc &lt;span class="k"&gt;in &lt;/span&gt;auth-service module-config-service ...&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;yq &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"del(.services[] | select(.name == &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;).github)"&lt;/span&gt; /tmp/spec.yaml
  yq &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"(.services[] | select(.name == &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)).image.registry_type = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;DOCR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /tmp/spec.yaml
  yq &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"(.services[] | select(.name == &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$svc&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;)).image.tag = &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$COMMIT_SHA&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; /tmp/spec.yaml
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Apply and deploy&lt;/span&gt;
doctl apps update &lt;span class="nv"&gt;$APP_ID&lt;/span&gt; &lt;span class="nt"&gt;--spec&lt;/span&gt; /tmp/spec.yaml
doctl apps create-deployment &lt;span class="nv"&gt;$APP_ID&lt;/span&gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is idempotent — on the first run it migrates from source to image, on subsequent runs it just updates the tag. The &lt;code&gt;doctl apps spec get&lt;/code&gt; preserves all secrets as encrypted values, so the update is safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 2: Parallelize CI Checks
&lt;/h2&gt;

&lt;p&gt;Our CI was running lint → typecheck → test → security sequentially in one job. We split them into parallel jobs that share a cached workspace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ci-setup (install + build packages, ~3 min)
  ├── ci-lint      (~3 min)
  ├── ci-typecheck  (~3 min)
  ├── ci-test       (~6 min)
  └── ci-security   (~2 min)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ci-setup&lt;/code&gt; job installs dependencies, builds shared packages, and saves the workspace to GitHub Actions cache. Each parallel job restores that cache and runs only its specific check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; We only build shared packages (&lt;code&gt;packages/*&lt;/code&gt;) in CI setup — not apps or services. Lint and typecheck work on source files. Tests use vitest which transpiles on-the-fly. The actual production builds happen in the Docker image matrix. This cut our setup step from 11 minutes to under 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 3: Remove Type-Checked ESLint Rules
&lt;/h2&gt;

&lt;p&gt;Our ESLint config used &lt;code&gt;parserOptions.project&lt;/code&gt; to enable type-checked rules like &lt;code&gt;no-floating-promises&lt;/code&gt; and &lt;code&gt;no-misused-promises&lt;/code&gt;. These rules require ESLint to spin up a full TypeScript type-checker — essentially running &lt;code&gt;tsc&lt;/code&gt; inside ESLint.&lt;/p&gt;

&lt;p&gt;Since we already run &lt;code&gt;tsc --noEmit&lt;/code&gt; as a separate typecheck step, these rules were making us pay for type-checking twice. Removing &lt;code&gt;parserOptions.project&lt;/code&gt; and the type-checked rules dropped lint from &lt;strong&gt;14 minutes to 3.5 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We also added &lt;code&gt;--cache&lt;/code&gt; to all ESLint scripts, which helps on local dev even though CI starts fresh each time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 4: Run CI and Image Builds Concurrently
&lt;/h2&gt;

&lt;p&gt;CI checks and Docker image builds have no dependency on each other. We run them in parallel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ci-setup ──&amp;gt; parallel CI checks ──┐
                                   ├──&amp;gt; deploy-staging
build-images (12 parallel) ───────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Image builds typically finish before CI (cached builds take ~2 min), so by the time tests pass, images are already waiting in DOCR.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Other Small Wins
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Merged CI and Security jobs&lt;/strong&gt; — security was a separate job that duplicated the entire install + build. Folding it into the CI flow saved 10-15 min.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency groups&lt;/strong&gt; with &lt;code&gt;cancel-in-progress: true&lt;/code&gt; — stale runs get cancelled when new commits are pushed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sentry shallow clone&lt;/strong&gt; — &lt;code&gt;fetch-depth: 100&lt;/code&gt; instead of full git history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vite &lt;code&gt;reportCompressedSize: false&lt;/code&gt;&lt;/strong&gt; in CI — small but free.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;th&gt;Improvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CI wall clock&lt;/td&gt;
&lt;td&gt;~35 min&lt;/td&gt;
&lt;td&gt;~9 min&lt;/td&gt;
&lt;td&gt;74% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DO deploy&lt;/td&gt;
&lt;td&gt;~30 min&lt;/td&gt;
&lt;td&gt;~7 min&lt;/td&gt;
&lt;td&gt;77% faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total pipeline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~60 min&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~16 min&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;73% faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker builds (first run)&lt;/td&gt;
&lt;td&gt;N/A (DO built)&lt;/td&gt;
&lt;td&gt;~8 min&lt;/td&gt;
&lt;td&gt;Cached after first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker builds (cached)&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;~2 min&lt;/td&gt;
&lt;td&gt;GHA layer cache&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pipeline now looks like this in GitHub Actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─ ci-setup (3 min) ─┬─ lint (3 min)
│                     ├─ typecheck (3 min)
│                     ├─ test (6 min)      ──┐
│                     └─ security (2 min)     ├── deploy (7 min)
└─ build-images x12 (2 min, parallel) ──────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What We Would Do Next
&lt;/h2&gt;

&lt;p&gt;If we needed to go even faster:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Vitest sharding&lt;/strong&gt; — split the test suite across 2-3 parallel runners&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript incremental&lt;/strong&gt; — &lt;code&gt;tsc --noEmit --incremental&lt;/code&gt; writes a &lt;code&gt;.tsbuildinfo&lt;/code&gt; file that speeds up subsequent type checks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selective image builds&lt;/strong&gt; — only rebuild services whose files actually changed using path filters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convert web to a service&lt;/strong&gt; — the React SPA still builds from source on DO (static sites do not support pre-built images). Converting it to an nginx service would let us pre-build it too&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But at 16 minutes from push to production, we are happy to ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;If your platform builds Docker images, build them yourself.&lt;/strong&gt; Managed platforms rarely cache Docker layers between deployments. Building in CI with &lt;code&gt;docker/build-push-action&lt;/code&gt; and GHA cache is dramatically faster.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do not run type-checking twice.&lt;/strong&gt; If you have &lt;code&gt;tsc --noEmit&lt;/code&gt; in your pipeline, you do not also need ESLint type-checked rules. Pick one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build once, share via cache.&lt;/strong&gt; A monorepo CI that installs and builds in every parallel job wastes most of its time on redundant setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Measure before optimizing.&lt;/strong&gt; Our initial assumption was that tests were the bottleneck. The real bottleneck was DigitalOcean rebuilding 12 Docker images from scratch on every deploy.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>performance</category>
      <category>cicd</category>
    </item>
    <item>
      <title>How We Built an AI Memory System That Actually Learns</title>
      <dc:creator>Keith Fawcett</dc:creator>
      <pubDate>Mon, 23 Mar 2026 09:31:02 +0000</pubDate>
      <link>https://forem.com/coherence_ai/how-we-built-an-ai-memory-system-that-actually-learns-1ndp</link>
      <guid>https://forem.com/coherence_ai/how-we-built-an-ai-memory-system-that-actually-learns-1ndp</guid>
      <description>&lt;h2&gt;
  
  
  How We Built an AI Memory System That Actually Learns
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;A deep dive into our multi-layered memory architecture — from vector embeddings to biographical peer cards — and what we learned from studying the best in the space.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: AI Agents That Forget Everything
&lt;/h3&gt;

&lt;p&gt;Most AI agent systems treat every interaction as a blank slate. Your agent completes a task, generates insights, discovers user preferences — and then forgets all of it. The next task starts from zero. This is the fundamental gap between a tool and a teammate: teammates learn.&lt;/p&gt;

&lt;p&gt;We set out to build a memory system for our autonomous agents that would make them genuinely learn over time — not just retrieve facts, but reason about what they've learned, identify patterns, and build an evolving understanding of the people they work with.&lt;/p&gt;

&lt;p&gt;Along the way, we studied platforms like &lt;a href="https://honcho.dev" rel="noopener noreferrer"&gt;Honcho&lt;/a&gt;, &lt;a href="https://mem0.ai" rel="noopener noreferrer"&gt;mem0&lt;/a&gt;, and &lt;a href="https://polsia.com" rel="noopener noreferrer"&gt;Polsia&lt;/a&gt; — each taking a different approach to AI memory. Honcho's concept of "dreaming" (periodic reflection on accumulated knowledge) and Polsia's three-tier memory hierarchy both heavily influenced our architecture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three Tiers of Memory: User, Account, Platform
&lt;/h3&gt;

&lt;p&gt;Before diving into implementation, it's worth understanding the scoping model. Inspired by Polsia's approach to memory hierarchy, we designed three distinct tiers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User-Scoped Memory&lt;/strong&gt; — Personal to each team member. When Nash (our autopilot agent) runs a personal autopilot cycle for you, it saves memories scoped to your UserId: your communication preferences, your pipeline priorities, the follow-ups you care about. These memories are invisible to other users' agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Account-Scoped Memory&lt;/strong&gt; — Shared across the entire workspace. Business facts, company strategy, market intelligence, customer patterns — things that benefit every team member and every agent in the organization. When Nash discovers that "enterprise deals close 40% faster when a technical champion is identified early," that's account-level knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform-Scoped Memory (Global Learning)&lt;/strong&gt; — Anonymized patterns that propagate across all accounts. When agents across different organizations independently discover the same operational insight, it gets promoted to the global layer. This creates a flywheel — the more accounts use the system, the smarter every agent gets out of the box.&lt;/p&gt;

&lt;p&gt;This three-tier model ensures privacy (personal memories stay personal), organizational knowledge compounds (account memories get richer), and the entire platform gets smarter over time (global learnings benefit everyone).&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture: Five Layers Deep
&lt;/h3&gt;

&lt;p&gt;Our memory system operates across five distinct layers, each serving a different purpose:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: Atomic Memories&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The foundation. Every observation, pattern, preference, fact, and episode gets stored as an individual memory with a 1536-dimensional vector embedding via pgvector. Each memory has confidence scoring, occurrence tracking, and category taxonomy.&lt;/p&gt;

&lt;p&gt;Key design choices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hash-partitioned by tenant (16 partitions) for physical isolation&lt;/li&gt;
&lt;li&gt;HNSW indexes per partition for fast cosine similarity search&lt;/li&gt;
&lt;li&gt;Content encrypted at rest (AES-256-GCM) while embeddings remain unencrypted for search&lt;/li&gt;
&lt;li&gt;Confidence + Occurrences fields that increase when the same insight is reinforced&lt;/li&gt;
&lt;li&gt;Agent-scoped memories (AgentId column) so agents can build self-knowledge — Nash accumulates memories about what approaches worked, which tools were most effective, and what strategies produced the best outcomes, carrying that self-knowledge forward across cycles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: Automatic Extraction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After every agent task, a lightweight LLM pass (Claude Haiku or GPT-5-mini) analyzes the execution output and decides what's worth remembering. The extraction is selective — trivial tasks like simple record updates are pre-filtered, and the LLM can explicitly declare "nothing worth remembering" for routine operations.&lt;/p&gt;

&lt;p&gt;For cycles that execute multiple tasks, we batch the extraction into a single LLM call. Instead of N separate passes, one pass reviews all outputs together, looking for cross-task patterns and themes. This typically reduces extraction cost by 60-70% during busy cycles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 3: Semantic Deduplication &amp;amp; Search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;rememberOrReinforce()&lt;/code&gt; method prevents memory bloat. Before creating a new memory, it searches for semantically similar existing memories (cosine similarity &amp;gt;= 0.9). If a match exists, it increments the occurrence count and boosts confidence instead of creating a duplicate. The system naturally converges — repeated patterns get stronger, not noisier.&lt;/p&gt;

&lt;p&gt;On the retrieval side, not every query needs the same depth of processing. A &lt;code&gt;synthesize&lt;/code&gt; parameter controls this: default mode returns a fast, cheap vector similarity search, while synthesize mode adds an LLM pass that weaves results into a coherent narrative — more expensive, but dramatically better for complex queries spanning multiple memories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 4: The Dreaming Job&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where the learning happens. Inspired by Honcho's concept of "sleep for a memory system," a background job runs periodically to reflect on accumulated knowledge:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consolidation:&lt;/strong&gt; Finds groups of semantically similar memories (0.85 threshold, looser than the 0.9 dedup threshold) and merges them via LLM into single, stronger statements. The originals get archived, and the consolidated memory inherits the sum of all occurrences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Promotion:&lt;/strong&gt; Observations reinforced 3+ times get promoted to "pattern" type with a confidence boost. The system recognizes that something noticed once is actually a reliable signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deduction Specialist:&lt;/strong&gt; For entities with enough accumulated data (50+ new conclusions since last dream), an LLM analyzes existing memories to identify logical implications, contradictions, and knowledge updates. If a user changed roles, the deduction pass catches the conflicting facts and resolves them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Induction Specialist:&lt;/strong&gt; Scans for recurring behavioral patterns across multiple observations. A preference must have evidence from at least two independent data points before it's promoted — single observations stay as observations.&lt;/p&gt;

&lt;p&gt;Both specialist passes feed results into the global learning layer when patterns are strong enough (high confidence + multiple occurrences), completing the user → account → platform propagation loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 5: Peer Cards (Biographical Profiles)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of requiring agents to search for user information every time, a stable biographical profile is &lt;strong&gt;always injected&lt;/strong&gt; into the agent's context. No similarity search needed, no query to get right — it's just there.&lt;/p&gt;

&lt;p&gt;Each peer card is a list of up to 40 atomic facts, prefixed by category:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Keith&lt;/span&gt;
&lt;span class="na"&gt;TIMEZONE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;US/Pacific&lt;/span&gt;
&lt;span class="na"&gt;PREFERENCE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Outcome-oriented execution without repeated approval-seeking&lt;/span&gt;
&lt;span class="na"&gt;PREFERENCE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Async communication over synchronous meetings&lt;/span&gt;
&lt;span class="na"&gt;INSTRUCTION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Do not create documents unless explicitly asked&lt;/span&gt;
&lt;span class="na"&gt;TRAIT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Delegates broadly to trusted agents&lt;/span&gt;
&lt;span class="na"&gt;INTEREST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AI agent architectures, autonomous systems&lt;/span&gt;
&lt;span class="na"&gt;ROLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Founder, technical product lead&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cards are auto-populated by the dreaming job's specialist passes, but can also be manually edited. They bootstrap automatically on first access with whatever data the system already knows.&lt;/p&gt;

&lt;p&gt;The key insight: &lt;strong&gt;separate stable biographical facts from transient memories&lt;/strong&gt;. A user's timezone doesn't change — it shouldn't be competing for relevance in a semantic search alongside yesterday's task results.&lt;/p&gt;

&lt;h3&gt;
  
  
  What We Learned
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Memory without reasoning is just storage.&lt;/strong&gt; Adding the dreaming job — especially the deduction and induction specialists — transformed our system from a recall engine into a learning engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three-tier scoping is essential.&lt;/strong&gt; Without it, you either over-share (personal preferences leak to other users) or under-share (valuable business patterns stay locked in one user's context). Polsia's hierarchy model was the right starting point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Peer cards are deceptively powerful.&lt;/strong&gt; The simplest feature (a list of strings) had the biggest impact on agent quality. When your agent always knows your name, timezone, and communication preferences, every interaction starts from a foundation of understanding instead of a blank slate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Threshold-based processing beats interval-based.&lt;/strong&gt; Triggering dreams based on conclusion counts + cooldown periods is smarter than a fixed interval. It ensures the system only does expensive reasoning when there's enough new data to justify it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Batch extraction is a free optimization.&lt;/strong&gt; Combining multiple task outputs into a single extraction pass doesn't just save tokens — it actually produces better memories because the LLM can identify cross-task patterns that individual extraction would miss.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Self-Improvement Loop: Closing the Feedback Gap
&lt;/h3&gt;

&lt;p&gt;A memory system that stores and retrieves is necessary but not sufficient. The missing piece was &lt;strong&gt;self-evaluation&lt;/strong&gt; — the agent needs to know whether it's actually getting better.&lt;/p&gt;

&lt;p&gt;We recently drew inspiration from two very different approaches to this problem:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.minimax.io/news/minimax-m27-en" rel="noopener noreferrer"&gt;MiniMax's M2.7 model&lt;/a&gt; uses a recursive self-evolution cycle where the model analyzes its own failure trajectories, modifies its scaffold code, evaluates results, and decides whether to keep or revert changes — running autonomously for 100+ iterations. Their Dev Harness architecture treats persistent memory, hierarchical skills, and evaluation infrastructure as first-class components of the training loop itself, not afterthoughts. Their &lt;a href="https://www.minimax.io/news/forge-scalable-agent-rl-framework-and-algorithm" rel="noopener noreferrer"&gt;Forge RL framework&lt;/a&gt; goes further, making context management an explicit action the agent learns through reinforcement — the agent decides what to remember, compress, or discard, rather than following hand-coded rules.&lt;/p&gt;

&lt;p&gt;Andrej Karpathy's &lt;a href="https://github.com/karpathy/autoresearch" rel="noopener noreferrer"&gt;autoresearch&lt;/a&gt; takes the opposite extreme: radical simplicity. One metric (&lt;code&gt;val_bpb&lt;/code&gt;), one file to edit (&lt;code&gt;train.py&lt;/code&gt;), and a strict hill-climbing loop — try a change, measure, keep or revert, repeat indefinitely. The human's job is to write the agent's instructions (&lt;code&gt;program.md&lt;/code&gt;), not the code. The key insight: an append-only results log (&lt;code&gt;results.tsv&lt;/code&gt;) gives the agent complete self-awareness about what's been tried and what worked.&lt;/p&gt;

&lt;p&gt;We took the core pattern common to both — &lt;strong&gt;measure → inject feedback → self-correct&lt;/strong&gt; — and applied it to our autopilot cycles:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cycle Quality Scoring:&lt;/strong&gt; After each autopilot cycle completes, a lightweight LLM call scores the cycle's performance on a 1-10 scale across four criteria: task relevance, output quality, efficiency, and improvement over previous cycles. The score and actionable feedback are persisted and injected into the next cycle's planning context, so the agent can see its own trajectory and adjust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured Performance Log:&lt;/strong&gt; A rolling 7-day view of task execution metrics — success rates by task type, token efficiency, tool error rates, and user ratings — compiled from the AgentTask table and injected alongside the quality scores. When Nash sees that research tasks average 45K tokens but one outlier consumed 89K, it self-corrects on scope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool Call Instrumentation:&lt;/strong&gt; Every tool invocation is wrapped with timing and success tracking. Per-tool breakdowns (call count, error count, average duration) flow through to the performance log, giving the agent visibility into its own operational efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent-Driven Memory:&lt;/strong&gt; Rather than always running an expensive LLM extraction pass after every task, we let the agent decide what's worth remembering in real-time. When the agent explicitly calls &lt;code&gt;saveMemory&lt;/code&gt; during execution, the post-task extraction is skipped in favor of a lightweight episode log. Agent-chosen memories are higher signal and lower cost — the agent develops intuition for what matters through the natural feedback of "I remembered X and it was useful later."&lt;/p&gt;

&lt;p&gt;The result is a closed loop: tool stats feed the performance log, the performance log feeds the quality scorer, and the quality scorer feeds the next planning cycle. Each cycle is slightly better-informed than the last.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next
&lt;/h3&gt;

&lt;p&gt;We're exploring several directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compound tools:&lt;/strong&gt; Higher-level skills that chain common tool sequences (research → create record → write doc), reducing token waste on re-discovering obvious patterns — inspired by MiniMax's hierarchical skill architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strategy documents:&lt;/strong&gt; Evolving the flat &lt;code&gt;FocusAreas&lt;/code&gt; text field into a structured, version-controlled strategy doc that the user and agent co-author over time — the &lt;code&gt;program.md&lt;/code&gt; pattern from autoresearch applied to business operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RL-informed context management:&lt;/strong&gt; Using quality scores and performance data to automatically tune which context blocks are most valuable, rather than injecting everything — taking a page from Forge's approach to learned context policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're excited about where this is heading. Every improvement to our memory system compounds — as more teams use Coherence, the agents get smarter, the global learning layer gets richer, and the feedback loops tighten. What Nash learns working with your business today makes it better for everyone tomorrow.&lt;/p&gt;

&lt;p&gt;Onward and upward.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
