<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Joshi Abhijeet on Medium]]></title>
        <description><![CDATA[Stories by Joshi Abhijeet on Medium]]></description>
        <link>https://medium.com/@jabhijeet?source=rss-2ef93af5eea4------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*dmbNkD5D-u45r44go_cf0g.png</url>
            <title>Stories by Joshi Abhijeet on Medium</title>
            <link>https://medium.com/@jabhijeet?source=rss-2ef93af5eea4------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 05:22:46 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@jabhijeet/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Beyond Design Patterns: Building Resilient, Scalable and Governed Enterprise Architecture]]></title>
            <link>https://medium.com/@jabhijeet/beyond-design-patterns-building-resilient-scalable-and-governed-enterprise-architecture-13a401bdf19a?source=rss-2ef93af5eea4------2</link>
            <guid isPermaLink="false">https://medium.com/p/13a401bdf19a</guid>
            <category><![CDATA[enterprise-architecture]]></category>
            <category><![CDATA[system-design-concepts]]></category>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[architecture]]></category>
            <category><![CDATA[leadership-architecture]]></category>
            <dc:creator><![CDATA[Joshi Abhijeet]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 14:18:55 GMT</pubDate>
            <atom:updated>2026-11-04T14:18:55.638Z</atom:updated>
            <content:encoded><![CDATA[<h3>An architecture leadership perspective on applying proven patterns to modern distributed systems</h3><p>In enterprise technology, architecture is often misunderstood as the exercise of selecting frameworks, defining technology standards, or producing system diagrams.</p><p>In reality, architecture is about making decisions that balance <strong>business agility, engineering productivity, operational resilience, security, and long-term sustainability</strong>.</p><p>As organizations modernize complex platforms, particularly in financial services, the challenge is rarely to build a new application from scratch. It is to evolve existing capabilities while maintaining business continuity, regulatory compliance, data integrity, and operational stability.</p><p>Design patterns provide a foundation for solving recurring engineering challenges. However, their real value emerges when they are applied as part of a coherent architectural strategy rather than as isolated technical solutions.</p><p>Consider a modern trading platform. It must process trades, integrate with risk and market data systems, maintain an auditable lifecycle, support reporting, and remain resilient when dependencies fail.</p><p>This article explores 13 architecture patterns through the lens of such a platform, focusing not only on how they work, but also on why, where, and when an architect should apply them.</p><h3>1. The Reference Architecture: A Modern Trading Platform</h3><p>Before exploring individual patterns, let us establish a reference architecture.</p><p>The following is an illustrative architecture — not a prescribed design. Actual implementation decisions depend on business requirements, latency expectations, regulatory obligations, existing technology, and operational constraints.</p><pre>flowchart TD<br>    Client[Trading Users / External Systems]<br>    Gateway[API Gateway]<br>    Trade[Trade Service]<br>    Risk[Risk Service]<br>    Portfolio[Portfolio Service]<br>    Reporting[Reporting Service]</pre><pre>    DB[(Trade Database)]<br>    Outbox[(Transactional Outbox)]<br>    Kafka[(Kafka Event Platform)]<br>    ReadDB[(Reporting Read Model)]</pre><pre>    Client --&gt; Gateway<br>    Gateway --&gt; Trade<br>    Gateway --&gt; Portfolio<br>    Gateway --&gt; Reporting</pre><pre>    Trade --&gt; DB<br>    Trade --&gt; Outbox<br>    Outbox --&gt; Kafka</pre><pre>    Kafka --&gt; Risk<br>    Kafka --&gt; Portfolio<br>    Kafka --&gt; ReadDB<br>    ReadDB --&gt; Reporting</pre><pre>    Risk --&gt; MarketData[Market Data Service]</pre><p>At a high level:</p><ul><li>The API Gateway provides controlled external access.</li><li>Trade Service owns trade lifecycle and business rules.</li><li>Risk and Portfolio services consume relevant trade events.</li><li>Kafka supports asynchronous communication.</li><li>The Transactional Outbox ensures reliable event publication.</li><li>Reporting uses a read-optimized model.</li><li>Resilience patterns protect critical services from dependency failures.</li></ul><p>This architecture is not about maximizing the number of microservices. It is about establishing clear ownership, appropriate boundaries, and reliable interactions.</p><h3>2. Application Architecture: Hexagonal Architecture</h3><p><strong>Category:</strong> Application Architecture</p><p><strong>Problem:</strong> How do we keep business logic independent of infrastructure and external systems?</p><p>Hexagonal Architecture, also known as Ports and Adapters, separates the application core from its external dependencies.</p><p>The domain and application logic define ports — interfaces through which external systems interact. Adapters implement those interfaces using technologies such as REST, Kafka, databases, or external APIs.</p><pre>flowchart LR<br>    REST[REST Controller]<br>    Kafka[Kafka Consumer]<br>    Core[Trade Application Core]<br>    Port[Application Ports]<br>    DB[Database Adapter]<br>    Risk[Risk API Adapter]</pre><pre>    REST --&gt; Core<br>    Kafka --&gt; Core<br>    Core --&gt; Port<br>    Port --&gt; DB<br>    Port --&gt; Risk</pre><h3>Trading platform application</h3><p>The Trade Service can expose trade creation through REST while also processing trade events from Kafka. Both entry points invoke the same application core.</p><p>The business rules remain independent of the transport mechanism.</p><h3>Architecture leadership perspective</h3><p>This pattern promotes:</p><ul><li>Testable business logic</li><li>Reduced infrastructure coupling</li><li>Technology flexibility</li><li>Cleaner ownership boundaries</li></ul><p>The key decision is not whether every application must use hexagonal architecture. It is whether the complexity and criticality of the domain justify the separation.</p><h3>3. API Gateway: Controlled Access to Distributed Capabilities</h3><p><strong>Category:</strong> Microservices / Communication Pattern</p><p><strong>Problem:</strong> How do we provide a consistent, secure entry point to distributed services?</p><p>An API Gateway acts as the entry point for external consumers. It can provide routing, authentication, authorization, rate limiting, request transformation, and observability.</p><pre>flowchart TD<br>    Consumers[Clients / External Systems]<br>    Gateway[API Gateway]<br>    Trade[Trade API]<br>    Portfolio[Portfolio API]<br>    Reporting[Reporting API]</pre><pre>    Consumers --&gt; Gateway<br>    Gateway --&gt; Trade<br>    Gateway --&gt; Portfolio<br>    Gateway --&gt; Reporting</pre><h3>Trading platform application</h3><p>Trading clients access Trade, Portfolio, and Reporting APIs through the gateway.</p><p>The gateway can enforce edge-level controls, while individual services remain responsible for their own business authorization.</p><h3>Architecture leadership perspective</h3><p>A gateway should simplify access and enforce common controls. It should not become a centralized business logic layer or a bottleneck for all internal communication.</p><h3>4. Resilience Architecture: Circuit Breaker, Bulkhead and Retry</h3><p>Distributed systems fail in ways that are often unpredictable. A downstream service may become slow, unavailable, overloaded, or temporarily unreachable.</p><p>Resilience is therefore not an afterthought. It is an architectural quality attribute.</p><h3>4.1 Circuit Breaker</h3><p><strong>Category:</strong> Resilience Pattern</p><p><strong>Purpose:</strong> Prevents repeated calls to an unhealthy dependency.</p><p>The circuit breaker uses three states:</p><ul><li>Closed: Requests flow normally.</li><li>Open: Requests fail fast or are blocked.</li><li>Half-Open: Limited requests test whether the dependency has recovered.</li></ul><pre>stateDiagram-v2<br>    [*] --&gt; Closed<br>    Closed --&gt; Open: Failure threshold reached<br>    Open --&gt; HalfOpen: Wait duration elapsed<br>    HalfOpen --&gt; Closed: Dependency healthy<br>    HalfOpen --&gt; Open: Failure continues</pre><p><strong>Trading platform example:</strong> If the Market Data service is unhealthy, the Risk Service can stop making repeated calls and apply an appropriate fallback or controlled failure response.</p><p>A fallback must be business-safe. Returning stale market data without explicit controls may be unacceptable for risk-sensitive operations.</p><h3>4.2 Bulkhead</h3><p><strong>Category:</strong> Resilience Pattern</p><p><strong>Purpose:</strong> Isolates resources to prevent cascading failures.</p><pre>flowchart TD<br>    Service[Trade Service]<br>    Pool1[Trade Processing Pool]<br>    Pool2[Market Data Pool]<br>    Pool3[Reporting Pool]</pre><pre>    Service --&gt; Pool1<br>    Service --&gt; Pool2<br>    Service --&gt; Pool3</pre><p>A slow dependency should not exhaust resources required by critical processing.</p><p><strong>Trading platform example:</strong> Market data requests and reporting queries can use separate resource pools from trade processing.</p><h3>4.3 Retry with Exponential Backoff</h3><p><strong>Category:</strong> Resilience Pattern</p><p><strong>Purpose:</strong> Recovers from transient failures.</p><p>Retry introduces another attempt after a temporary failure. Exponential backoff increases the delay between retries, usually with jitter and a maximum attempt limit.</p><pre>flowchart TD<br>    Request[Request]<br>    Attempt[Execute Operation]<br>    Success{Successful?}<br>    RetryLimit{Retry Limit Reached?}<br>    Delay[Wait with Backoff]<br>    Fail[Return Controlled Failure]</pre><pre>    Request --&gt; Attempt<br>    Attempt --&gt; Success<br>    Success --&gt;|Yes| Done[Complete]<br>    Success --&gt;|No| RetryLimit<br>    RetryLimit --&gt;|No| Delay<br>    Delay --&gt; Attempt<br>    RetryLimit --&gt;|Yes| Fail</pre><p><strong>Trading platform example:</strong> A temporary network failure while publishing a notification may be retried.</p><p>Retries must be carefully designed around idempotency. Blind retries of trade creation or payment operations can create duplicate business transactions.</p><h3>Architecture leadership perspective</h3><p>Resilience patterns should be applied based on failure scenarios, not uniformly across all services.</p><p>The architecture should define:</p><ul><li>Which dependencies are critical</li><li>Acceptable latency and failure thresholds</li><li>Timeout and retry budgets</li><li>Fallback behavior</li><li>Idempotency requirements</li><li>Monitoring and alerting expectations</li></ul><h3>5. Sidecar and Service Discovery: Infrastructure for Distributed Services</h3><h3>5.1 Sidecar Pattern</h3><p><strong>Category:</strong> Deployment / Infrastructure Pattern</p><p><strong>Purpose:</strong> Provides supporting capabilities alongside an application.</p><pre>flowchart LR<br>    subgraph Pod<br>        App[Trade Service]<br>        Sidecar[Proxy / Telemetry Sidecar]<br>    end</pre><pre>    App &lt;--&gt; Sidecar<br>    Sidecar --&gt; Other[Other Services]</pre><p>A sidecar can provide service proxying, mTLS, telemetry, or logging without embedding those capabilities into the application code.</p><p><strong>Trading platform example:</strong> An Envoy sidecar may manage service-to-service communication and telemetry.</p><p>A sidecar is not mandatory for every workload. It introduces additional resource usage and operational complexity.</p><h3>5.2 Service Discovery</h3><p><strong>Category:</strong> Microservices Infrastructure Pattern</p><p><strong>Purpose:</strong> Locates service instances dynamically.</p><pre>flowchart TD<br>    Client[Trade Service]<br>    Registry[Service Registry / DNS]<br>    Risk1[Risk Service Instance 1]<br>    Risk2[Risk Service Instance 2]<br>    Risk3[Risk Service Instance 3]</pre><pre>    Client --&gt; Registry<br>    Registry --&gt; Risk1<br>    Registry --&gt; Risk2<br>    Registry --&gt; Risk3</pre><p>In dynamic environments, instances may be created or removed frequently. Service discovery allows clients or infrastructure components to locate available instances without hardcoded addresses.</p><p><strong>Trading platform example:</strong> Kubernetes DNS or service discovery mechanisms can help locate Risk Service instances.</p><h3>Architecture leadership perspective</h3><p>The goal is to standardize infrastructure capabilities where they improve consistency and reduce operational burden — without forcing unnecessary technology into every application.</p><h3>6. Transactional Outbox: Reliable Event Publication</h3><p><strong>Category:</strong> Data Consistency / Messaging Pattern</p><p><strong>Problem:</strong> How do we avoid inconsistencies between a database transaction and event publication?</p><p>Consider a trade creation operation:</p><ol><li>The trade is saved in PostgreSQL.</li><li>A TradeCreated event is published to Kafka.</li></ol><p>If the database write succeeds but Kafka publication fails, downstream systems never receive the event.</p><p>The Transactional Outbox Pattern addresses this by persisting the business change and event record within the same database transaction.</p><pre>flowchart TD<br>    Request[Create Trade Request]<br>    Service[Trade Service]<br>    Transaction[Database Transaction]<br>    TradeDB[(Trade Table)]<br>    Outbox[(Outbox Table)]<br>    Publisher[Outbox Publisher / CDC]<br>    Kafka[(Kafka)]</pre><pre>    Request --&gt; Service<br>    Service --&gt; Transaction<br>    Transaction --&gt; TradeDB<br>    Transaction --&gt; Outbox<br>    Outbox --&gt; Publisher<br>    Publisher --&gt; Kafka</pre><h3>Trading platform application</h3><p>The Trade Service stores the trade and its TradeCreated event atomically. A CDC or publisher process subsequently delivers the event to Kafka.</p><h3>Architecture leadership perspective</h3><p>The Outbox Pattern improves reliability but does not automatically guarantee end-to-end exactly-once business processing.</p><p>Architects must also consider:</p><ul><li>Idempotent consumers</li><li>Event ordering</li><li>Duplicate handling</li><li>Schema evolution</li><li>Monitoring of outbox lag</li><li>Replay and recovery</li></ul><h3>7. Saga: Coordinating Distributed Business Transactions</h3><p><strong>Category:</strong> Distributed Transaction Pattern</p><p><strong>Problem:</strong> How do we coordinate business workflows across independent services without a global database transaction?</p><p>The Saga Pattern breaks a distributed workflow into local transactions. Each step can have a compensating action if the workflow cannot complete.</p><p>Two approaches are common:</p><ul><li>Choreography: Services coordinate through events.</li><li>Orchestration: A central coordinator manages the workflow.</li></ul><pre>flowchart TD<br>    Start[Start Workflow]<br>    Order[Create Order]<br>    Payment[Authorize Payment]<br>    Inventory[Reserve Inventory]<br>    Complete[Complete Order]</pre><pre>    CancelPayment[Compensate: Cancel Payment]<br>    CancelOrder[Compensate: Cancel Order]</pre><pre>    Start --&gt; Order<br>    Order --&gt; Payment<br>    Payment --&gt; Inventory<br>    Inventory --&gt; Complete</pre><pre>    Inventory -. Failure .-&gt; CancelPayment<br>    CancelPayment --&gt; CancelOrder</pre><h3>Trading platform application</h3><p>For a business workflow involving trade capture, confirmation, and downstream processing, a Saga-like approach may coordinate steps across services where local transactions are insufficient.</p><p>However, not every trading workflow should be modeled as a Saga. Some operations require strict business controls, specialized lifecycle handling, or explicit reconciliation.</p><h3>Architecture leadership perspective</h3><p>The architect must distinguish between technical rollback and business compensation. A compensating action does not necessarily erase a previous business event; it may create a new event that corrects the state.</p><h3>8. CQRS: Separating Read and Write Responsibilities</h3><p><strong>Category:</strong> Architectural Pattern</p><p><strong>Problem:</strong> How do we optimize transactional processing and reporting independently?</p><p>CQRS separates the command model, which changes state, from the query model, which retrieves information.</p><pre>flowchart LR<br>    Command[Commands]<br>    WriteModel[Trade Write Model]<br>    WriteDB[(Trade Database)]<br>    Events[Trade Events]<br>    ReadModel[Portfolio / Reporting Read Model]<br>    ReadDB[(Read Database)]<br>    Query[Queries]</pre><pre>    Command --&gt; WriteModel<br>    WriteModel --&gt; WriteDB<br>    WriteModel --&gt; Events<br>    Events --&gt; ReadModel<br>    ReadModel --&gt; ReadDB<br>    Query --&gt; ReadModel</pre><h3>Trading platform application</h3><p>The Trade Service manages transactional trade updates. Portfolio and Reporting services consume events to maintain query-optimized models.</p><h3>Architecture leadership perspective</h3><p>CQRS can improve scalability and read performance, but it introduces synchronization complexity and eventual consistency.</p><p>It should be introduced when the read/write workloads or domain requirements justify it — not simply because the system uses microservices.</p><h3>9. Event Sourcing: Preserving the Business Lifecycle</h3><p><strong>Category:</strong> Data Architecture Pattern</p><p><strong>Problem:</strong> How do we preserve the sequence of changes that led to the current state?</p><p>Event Sourcing stores state changes as events. The current state can be reconstructed by replaying those events.</p><pre>flowchart TD<br>    Events[Trade Event Stream]<br>    E1[TradeCreated]<br>    E2[TradeAmended]<br>    E3[TradeConfirmed]<br>    E4[TradeCancelled]<br>    Replay[State Reconstruction]<br>    State[Current Trade State]</pre><pre>    Events --&gt; E1<br>    Events --&gt; E2<br>    Events --&gt; E3<br>    Events --&gt; E4<br>    E1 --&gt; Replay<br>    E2 --&gt; Replay<br>    E3 --&gt; Replay<br>    E4 --&gt; Replay<br>    Replay --&gt; State</pre><h3>Trading platform application</h3><p>A trade lifecycle may include:</p><ul><li>TradeCreated</li><li>TradeAmended</li><li>TradeConfirmed</li><li>TradeCancelled</li></ul><p>The state can be reconstructed from the sequence of events.</p><h3>Architecture leadership perspective</h3><p>Event Sourcing can provide strong historical traceability, but it also introduces event versioning, replay, storage, retention, and operational complexity.</p><p>It should not be confused with simply storing events in Kafka or maintaining an audit table. Event Sourcing makes the event stream the authoritative source of state.</p><h3>10. Strangler Fig: Modernizing Legacy Platforms Incrementally</h3><p><strong>Category:</strong> Modernization Pattern</p><p><strong>Problem:</strong> How do we evolve legacy applications without a risky, large-scale rewrite?</p><p>The Strangler Fig Pattern incrementally replaces functionality in a legacy application with modern services.</p><p>A routing layer directs requests to the legacy system or the new implementation.</p><pre>flowchart TD<br>    Client[Client]<br>    Router[Routing Layer]<br>    Legacy[Legacy Trading Platform]<br>    NewService[Modern Trade Service]<br>    NewRisk[Modern Risk Service]</pre><pre>    Client --&gt; Router<br>    Router --&gt; Legacy<br>    Router --&gt; NewService<br>    NewService --&gt; NewRisk</pre><p>As capabilities are migrated, traffic is gradually redirected toward the new platform.</p><h3>Trading platform application</h3><p>A legacy monolithic trading platform may be modernized by extracting trade validation, reporting, or selected risk capabilities.</p><h3>Architecture leadership perspective</h3><p>Modernization is a business transformation, not only a technology migration.</p><p>It requires:</p><ul><li>Capability-level decomposition</li><li>Dependency analysis</li><li>Data migration strategy</li><li>Coexistence and reconciliation</li><li>Incremental delivery</li><li>Clear legacy retirement criteria</li></ul><p>The objective is to reduce risk while continuously delivering business value.</p><h3>11. Blue-Green and Canary Deployments: Safer Production Releases</h3><p><strong>Category:</strong> Deployment Patterns</p><p><strong>Problem:</strong> How do we reduce the operational risk of releasing software?</p><h3>Blue-Green Deployment</h3><p>Two production-like environments are maintained. Traffic is switched from the current version to the new version after validation.</p><h3>Canary Deployment</h3><p>A small percentage of traffic is directed to the new version. If monitoring confirms acceptable behavior, traffic is gradually increased.</p><pre>flowchart TD<br>    Traffic[Production Traffic]<br>    Router[Traffic Router]<br>    Stable[Stable Version]<br>    Canary[New Canary Version]<br>    Monitor[Metrics / Business Validation]</pre><pre>    Traffic --&gt; Router<br>    Router --&gt; Stable<br>    Router --&gt; Canary<br>    Canary --&gt; Monitor<br>    Monitor --&gt; Decision{Healthy?}<br>    Decision --&gt;|Yes| Increase[Increase Traffic]<br>    Decision --&gt;|No| Rollback[Rollback Canary]</pre><h3>Trading platform application</h3><p>A new trade validation service can be exposed to a controlled percentage of traffic before a wider rollout.</p><p>Monitoring should include not only infrastructure metrics but also business correctness, error rates, latency, and downstream impacts.</p><h3>Architecture leadership perspective</h3><p>Deployment safety is an architectural capability. It requires observability, automated validation, backward-compatible changes, and reliable rollback strategies.</p><h3>12. Bringing the Patterns Together</h3><p>The patterns discussed above are not independent technical choices. They form a set of complementary architectural capabilities.</p><p>Architecture concernPatternsBusiness / Engineering OutcomeApplication modularityHexagonal ArchitectureMaintainable, testable business logicControlled accessAPI GatewayConsistent edge security and routingInfrastructure separationSidecar, Service DiscoveryFlexible service operationsResilienceCircuit Breaker, Bulkhead, RetryReduced blast radius and improved recoveryData consistencyTransactional OutboxReliable event publicationDistributed workflowsSagaCoordinated business processesRead/write optimizationCQRSWorkload-specific performanceHistorical stateEvent SourcingLifecycle reconstruction and traceabilityLegacy modernizationStrangler FigIncremental transformationRelease safetyBlue-Green, CanaryControlled production change</p><p>A pattern should be adopted only when its benefits outweigh its costs.</p><p>For example:</p><ul><li>CQRS may be justified for high-volume reporting but unnecessary for a simple CRUD application.</li><li>Event Sourcing may be valuable for lifecycle reconstruction but excessive for domains where a conventional database and audit history are sufficient.</li><li>A Circuit Breaker can protect a dependency, but it does not replace timeout configuration or business-level fallback decisions.</li><li>A Saga can coordinate distributed workflows, but it does not provide the same guarantees as a single ACID transaction.</li></ul><p>The architect’s responsibility is to make these trade-offs explicit.</p><h3>13. Architecture Leadership: From Patterns to Enterprise Capability</h3><p>At an enterprise scale, architecture effectiveness is not measured by the number of patterns adopted or diagrams produced.</p><p>It is measured by the outcomes architecture enables.</p><p>A mature architecture function should focus on the following dimensions.</p><h3>1. Business alignment</h3><p>Architecture decisions should support business strategy, product priorities, and operational requirements.</p><h3>2. Reuse and standardization</h3><p>Common capabilities — security, observability, messaging, deployment, and resilience — should be made available through reusable architecture assets and engineering templates.</p><h3>3. Governance by design</h3><p>Architecture principles should be embedded into engineering workflows wherever practical. Automated controls can validate standards earlier in the development lifecycle, reducing late-stage review effort.</p><h3>4. Resilience and operational excellence</h3><p>Failure scenarios should be designed for explicitly. Critical dependencies, recovery expectations, service-level objectives, and operational ownership should be clear.</p><h3>5. Modernization with measurable outcomes</h3><p>Modernization should be prioritized based on business value, risk reduction, cost, and delivery impact — not technology novelty.</p><h3>6. Architecture metrics</h3><p>Useful measures include:</p><ul><li>Architecture review turnaround time</li><li>Reuse of approved architecture assets</li><li>Deployment frequency and change failure rate</li><li>Service reliability and recovery time</li><li>Technical debt reduction</li><li>Security and compliance control coverage</li><li>Engineering productivity</li></ul><p>Metrics should be interpreted in context. No single metric can represent architecture quality.</p><h3>Conclusion: Architecture Is About Decisions, Not Patterns</h3><p>Enterprise architecture is ultimately the discipline of making informed decisions under constraints.</p><p>Patterns such as Hexagonal Architecture, Circuit Breaker, Transactional Outbox, CQRS, and Strangler Fig provide proven approaches to recurring challenges. Yet their successful application depends on understanding the business context, system boundaries, failure modes, operational capabilities, and long-term consequences.</p><p>For architecture leaders, the real opportunity is to move beyond documenting patterns and toward creating an environment where engineering teams can make consistent, secure, resilient, and effective decisions.</p><p><strong>The best architecture is not the one with the most patterns. It is the one that enables the business to evolve while keeping complexity, risk, and operational cost under control.</strong></p><p><em>What architecture patterns have made the greatest difference in your enterprise systems? And which patterns have introduced more complexity than value?</em></p><p>#SoftwareArchitecture #EnterpriseArchitecture #SystemDesign #DistributedSystems #Microservices #Java #ArchitectureLeadership #Modernization #ResilienceEngineering #TradingTechnology</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=13a401bdf19a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[I Spent a Week Designing the “Perfect” Code-RAG Engine. Then the Research Told Me to Delete It.]]></title>
            <link>https://medium.com/@jabhijeet/i-spent-a-week-designing-the-perfect-code-rag-engine-then-the-research-told-me-to-delete-it-5812362e9a5c?source=rss-2ef93af5eea4------2</link>
            <guid isPermaLink="false">https://medium.com/p/5812362e9a5c</guid>
            <category><![CDATA[knowledge-repo]]></category>
            <category><![CDATA[code]]></category>
            <category><![CDATA[rags]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[lsp]]></category>
            <dc:creator><![CDATA[Joshi Abhijeet]]></dc:creator>
            <pubDate>Mon, 15 Jun 2026 11:00:34 GMT</pubDate>
            <atom:updated>2026-06-18T00:29:18.773Z</atom:updated>
            <content:encoded><![CDATA[<h3>A story about building a code-context tool for AI agents — and discovering that the best version of it barely uses the thing I’d centered it on.</h3><p>The code : <a href="https://github.com/jabhijeet/coderag">https://github.com/jabhijeet/coderag</a></p><p>It started, as these things do, with a clean idea.</p><p>I wanted to solve the problem every AI coding agent runs into: <strong>context.</strong> When you ask Claude or Codex or Cline to fix a bug, the naive move is to stuff a pile of files into the prompt. That burns tokens, drowns the model in noise, and still often misses the one function that mattered. The obvious fix — the one half the industry reached for — is RAG: embed the whole codebase into vectors, retrieve the most similar chunks, hand those to the model.</p><p>So I set out to design a production-grade version of that. A tool I called coderag: a local server that indexes your repo into embeddings and serves precise snippets to any agent over MCP. Language-agnostic, framework-agnostic, agent-agnostic. The works.</p><p>What follows is the design journey — the good decisions, the over-engineering, the moment a literature review quietly demolished my central premise, and the architecture I’d actually build now. If you’re building anything that feeds code to an LLM, I think the destination matters more than the route.</p><h3>Act I: Building the “right” RAG engine</h3><p>The early decisions felt solid, and most of them survived to the end, so they’re worth stating.</p><p><strong>Tree-sitter, not fixed-size chunks.</strong> You don’t cut code into 512-token windows at arbitrary boundaries — you split along structural lines: functions, classes, methods. A chunk should be a <em>thing</em>, not a slice. Tree-sitter has grammars for nearly every language, which is also what makes “language-agnostic” real instead of aspirational.</p><p><strong>Two-stage retrieval.</strong> Embedding similarity (“bi-encoder”) is fast but mediocre at judging true relevance. So you over-fetch candidates with the cheap vector search, then <strong>rerank</strong> them with a cross-encoder that reads the query and each candidate <em>together</em>. Recall first, precision second. This is the single biggest quality lever in classical RAG, and I built the whole pipeline around it.</p><p><strong>Optimal, not maximal, context.</strong> A counterintuitive lesson from the research I was leaning on: more retrieved chunks made answers <em>worse</em>, not better. Irrelevant context actively degrades the model. The goal isn’t to fill the window — it’s to land the one discriminating fact and nothing else.</p><p><strong>MCP over stdio.</strong> By speaking the Model Context Protocol, the tool works with every agent — Claude Code, Cline, Cursor, Codex — with zero per-agent code. You implement the spec, not the client.</p><p>So far, so good. Then I made the decision that felt clever and turned out to be the flaw.</p><h3>Act II: The seductive bad idea — “commit the index”</h3><p>Here was my pitch to myself: <em>if the embeddings live in the repo, committed to git, then every teammate and every CI run gets a warm index for free. No re-embedding. Clone and go.</em></p><p>I went deep on making this work. A deterministic on-disk format so two machines produce byte-identical output. Content-addressed chunks so an unchanged function keeps its vector across reindexes. Per-file shards so a one-line edit touches one small file instead of rewriting a monolith. A .gitattributes strategy, a merge-conflict resolution rule, crash-safe atomic writes, a single-writer lock.</p><p>It was, in fairness, <em>good engineering.</em> It was also solving a problem I’d invented.</p><p>Then I kept hardening — non-functional requirements (timeouts, large files, monorepo scale, permissions), a security model (treat the committed index as untrusted input, because a malicious commit could poison it), a reranker as the quality centerpiece. Every pass made the spec more rigorous. None of them questioned the foundation.</p><p>That’s the trap with momentum: you polish the thing instead of interrogating it.</p><h3>Act III: The question that broke it</h3><p>After a lot of this, I stopped and asked the uncomfortable version of the question:</p><blockquote>Is embedding-first retrieval even the right way to give code to an LLM? Or am I optimizing a local maximum?</blockquote><p>I had a hunch the framing was off. The strongest coding agents I’d seen didn’t feel like they were doing vector lookups — they felt like they were <em>navigating</em>: grep for a symbol, read a file, follow an import, grep again. So I ran a proper literature-and-product review. What came back reframed the entire project.</p><p><strong>The decisive finding:</strong> Anthropic built RAG-with-a-vector-database into early versions of Claude Code — then ripped it out. In the team’s own words, agentic search “works better… simpler and doesn’t have the same issues around security, privacy, staleness, and reliability.” Claude Code today ships ripgrep and <em>does not index your codebase or use embeddings at all.</em> (The team candidly admitted the comparison was “mostly vibes plus some internal benchmarks” — but it’s corroborated everywhere you look.)</p><p>And it wasn’t just them:</p><ul><li><strong>Sourcegraph</strong> replaced Cody’s embeddings with plain code search, citing scaling, security, and maintenance.</li><li><strong>Aider</strong> uses a tree-sitter “repo map” ranked by PageRank — signatures, no embeddings.</li><li><strong>Cline, Codex CLI, Zed</strong> — no embedding index.</li><li><strong>Cursor and Copilot</strong> <em>do</em> keep embeddings — but they store them remotely, never commit them, and <em>also</em> built fast exact-text search because, in Cursor’s words, regex search is “a hot path in the agent’s reasoning loop.”</li></ul><p>The punchline I couldn’t dodge: <strong>no production tool I found commits its vector index to git.</strong> The single most distinctive thing about my design was something nobody ships.</p><h3>Why code is different from documents</h3><p>Here’s the insight that made it click. General-document RAG and <em>code</em> retrieval are not the same problem.</p><p>A lot of code retrieval is about <strong>exact symbols, not fuzzy meaning.</strong> “Where is authenticate() called?&quot; has an exact answer. Grep finds it in milliseconds, perfectly, every time. An embedding model gives you things that are <em>semantically near</em> authenticate — which is precisely wrong when you needed the call sites.</p><p>Embeddings genuinely win on the <em>conceptual</em> query: “where is auth handled?” — no single keyword, a fuzzy notion spread across files. That’s real, and it’s worth keeping. But it’s the minority of what an agent actually asks.</p><p>And then there’s the killer: <strong>freshness.</strong> An editing agent’s most common read is of code <em>it just wrote.</em> A precomputed index — especially a committed one — is stale exactly when it matters most. The agent searches for text that isn’t in the index yet, finds nothing, and wanders off on a token-burning goose chase. Exact tools reading the live file system never have this problem. They always see the truth.</p><p>Staleness wasn’t a tuning problem I could fix. It was structural. And I’d architected my whole tool around the stale thing.</p><h3>Act IV: Inverting the architecture</h3><p>So I flipped it. Same good parts, opposite foundation.</p><p><strong>Before:</strong> embeddings are the product; the committed vector store is the heart.</p><p><strong>After:</strong> <em>exact tools</em> are the product; embeddings are an optional, local, never-committed fallback.</p><p>Concretely, the revised coderag exposes a small toolbox the agent drives itself:</p><ul><li>search_code — fast indexed regex/literal search (ripgrep&#39;s engine), always reading the live tree. The default, most-used tool.</li><li>repo_map — tree-sitter <strong>signature skeletons</strong>: the shape of a file (function and class signatures) without the bodies. The cheap &quot;what&#39;s in here&quot; view; bodies fetched on demand.</li><li>find_symbol / find_references / goto_definition — precise resolution via a language server (LSP) where available, tree-sitter fallback otherwise. The exact cross-file answers embeddings blur.</li><li>read_span — exact line ranges, never whole files.</li><li>semantic_search — the <em>optional</em> embedding fallback, off by default, local-only, for the genuinely fuzzy conceptual queries. The reranker pipeline I loved still lives here — just demoted from foundation to fallback.</li></ul><p>The cache that powers the symbol map (and, if you opt in, the vectors) is <strong>git-ignored and disposable.</strong> If it’s ever wrong, you delete it and rebuild. An entire class of hard problems — determinism, merge conflicts, git bloat, “refuse to serve on model mismatch” — simply evaporated. The hardest engineering in my original spec turned out to be unnecessary the moment I stopped committing the index.</p><p>There’s a bonus, too. The thing that <em>actually</em> saves tokens isn’t a clever retrieval scheme — it’s <strong>prompt caching</strong> at the API layer (keeping a stable prompt prefix across turns) plus returning small spans instead of files. A vector store is neither necessary nor sufficient for that. The revised design leans into the levers that matter.</p><h3>What I kept, and what I’d tell you</h3><p>This wasn’t a rewrite from zero, and that’s the point. Tree-sitter chunking, the recall-then-rerank pipeline, content-addressed incremental updates, MCP-over-stdio, the agent-agnostic CLI, the fail-soft robustness, the security model — all survived. What inverted was <strong>priority and persistence</strong>: exact tools became primary, embeddings optional, and the store stopped being a committed artifact and became a throwaway local cache. In build terms, a complete, useful, offline, zero-config exact-tools server now ships <em>before a single line of embedding code is written.</em></p><p>A few lessons I’m taking with me:</p><ol><li><strong>Match the retrieval to the data.</strong> Code is full of exact symbols. Fuzzy semantic matching is a liability for most code questions and a genuine asset for a few. Build for the common case; keep the asset as a fallback.</li><li><strong>Freshness can outrank cleverness.</strong> For an agent editing code, “always reflects the current bytes” beats “semantically sophisticated but lagging.” Some properties are structural; you can’t tune your way out of staleness.</li><li><strong>Interrogate the foundation, not just the details.</strong> I hardened a flawed premise for a long time before questioning it. Polishing is not the same as validating. The cheapest moment to challenge your core assumption is <em>before</em> you’ve built a beautiful spec on top of it.</li><li><strong>Don’t throw away the good parts when the thesis changes.</strong> The reranker, the structural chunking, the protocol choice — all still good. Inverting an architecture is usually re-prioritization, not demolition.</li><li><strong>The best evidence is what teams <em>removed</em>.</strong> Anyone can add a feature in a blog post. When multiple serious teams <em>rip out</em> the exact thing you’re building, that’s the signal worth its weight.</li></ol><p>The irony isn’t lost on me: I set out to build the perfect code-RAG engine, and the better tool barely does RAG. It mostly hands the model a flashlight and lets it look. Turns out that’s what the strongest agents were doing all along.</p><p><em>If you’re building code-context tooling for agents: start with exact tools — grep, tree-sitter symbols, LSP — keep them fresh, return small spans, lean on prompt caching, and add semantic search only as a measured, optional fallback. And whatever you do, don’t commit the index.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5812362e9a5c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An AI-Driven Framework for Improving Targeted Delivery of Government Welfare Schemes
Abstract…]]></title>
            <link>https://medium.com/@jabhijeet/an-ai-driven-framework-for-improving-targeted-delivery-of-government-welfare-schemes-abstract-d4a588d702a5?source=rss-2ef93af5eea4------2</link>
            <guid isPermaLink="false">https://medium.com/p/d4a588d702a5</guid>
            <category><![CDATA[ai-in-public-service]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[government]]></category>
            <dc:creator><![CDATA[Joshi Abhijeet]]></dc:creator>
            <pubDate>Tue, 17 Feb 2026 15:06:25 GMT</pubDate>
            <atom:updated>2026-02-18T12:39:49.353Z</atom:updated>
            <content:encoded><![CDATA[<h3><em>An AI-Driven Framework for Improving Targeted Delivery of Government Welfare Schemes</em></h3><h3>Abstract</h3><p>Government welfare schemes play a crucial role in socio-economic development, yet a persistent challenge across many regions is the mismatch between intended beneficiaries and actual recipients. This gap arises due to fragmented data, complex eligibility criteria, lack of awareness among citizens, and inefficiencies in manual verification processes. This paper proposes an AI-driven decision support framework designed to improve the identification of eligible beneficiaries and enhance citizen access to relevant government schemes through a unified intelligent application.</p><h3>Introduction</h3><p>Despite the availability of numerous public welfare programs, studies and audits frequently reveal issues such as exclusion errors (eligible beneficiaries not receiving benefits) and inclusion errors (ineligible recipients gaining access). Traditional rule-based systems and manual audits struggle to scale with population size and policy complexity. Advances in artificial intelligence and machine learning offer an opportunity to automate eligibility analysis, discover data inconsistencies, and personalize scheme recommendations for citizens.</p><h3>Proposed System Overview</h3><p>The proposed system consists of a dual-access AI application serving two primary user groups:<br>Government Officials:<br>Officials can analyze beneficiary datasets to identify missing, duplicate, or potentially ineligible beneficiaries. The system assists in policy compliance checks, beneficiary audits, and scheme performance evaluation.<br>Beneficiaries (Citizens):<br>Citizens can input socio-economic and demographic information to receive personalized recommendations of relevant government schemes along with explanations of eligibility and required documentation.<br>Data Sources and Training Corpus<br>The training data comprises:<br>Structured information on government schemes, including eligibility rules, benefit thresholds, and geographic constraints.<br>Publicly available socio-economic datasets such as census data, income slabs, employment records, and anonymized beneficiary records.<br>All data usage is designed to comply with privacy, anonymization, and ethical AI standards.</p><h3>Machine Learning Approach</h3><p>The system adopts a hybrid AI strategy:<br>Supervised Classification Models (e.g., Naïve Bayes, Logistic Regression, Support Vector Machines) are used to predict eligibility and flag anomalies in beneficiary records.<br>Unsupervised Clustering Techniques (e.g., K-Means) help identify hidden population segments, underserved groups, and unusual enrollment patterns.<br>Reinforcement Learning can be applied iteratively to optimize scheme recommendation accuracy based on feedback loops, policy changes, and observed outcomes.</p><h3>Expected Impact</h3><p>The proposed framework aims to reduce leakage in welfare distribution, improve scheme reach, and increase transparency in governance. By aligning data-driven intelligence with public policy objectives, the system has the potential to significantly enhance trust, efficiency, and equity in government service delivery.</p><h3>Conclusion</h3><p>This research outlines a scalable AI-based foundation for improving welfare scheme targeting. Future work will focus on real-world pilots, fairness evaluation, explainable AI techniques, and longitudinal impact assessment.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d4a588d702a5" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>