I Built a Cyber Insurance Platform for Australia's Largest Insurer from Indonesia — Here's the Full Architecture

One lone engineer. ONE month. Now it's the invincible backbone for 4.1 million microfinance customers. The completely raw story of how I hand-coded an offline-first architecture for 5,000 field officers in Indonesia's deepest corners — and the brutal lessons of being a Single Point of Failure.
"Technology is best when it fundamentally brings people together." — Matt Mullenweg
The entire backend system of this project was built solely by me (single-fighter) from scratch — including the architecture, APIs, message brokers, databases, sync engine, and deployment — with a target MVP of just 1 month. There were no other backend engineers. The frontend mobile side (React Native) was handled by a separate team.
Project Tepati is the digital transformation of the Sharia microfinance process for one of the largest Sharia banks in Indonesia. This system replaced the manual paper-based process with a digital end-to-end application used by 5,000+ Community Officers across Indonesia.
This article dissects the most challenging features from both the engineering and business sides.
The field officers (Community Officers) work in remote areas — from the corners of Sumatra to isolated villages in Nusa Tenggara. Internet connection is a luxury, not a certainty.
Before Tepati, the customer acquisition process was conducted using basic Microsoft Power Apps. Field officers filled out data through Power Apps forms, sent them manually via Teams, and waited for manual approval. This process was slow because:
Before Tepati, 23% of applications failed because data was lost or incomplete. Every failure meant the officer had to physically return to the customer — averaging an additional 2-3 wasted days and severely damaging customer trust.
Many ask: how could one person build a backend system this size in a month?
The honest answer: prioritization and technology leverage.
The key was my familiarity with Golang and the message broker ecosystem (Kafka, AMQ, MQTT). There was zero learning curve. Every architectural decision was made to maximize velocity without sacrificing reliability. The frontend team simply consumed the APIs I provided.
The Problem: Officers frequently enter no-signal zones for 4-6 hours.
The Solution: I built an architecture that is always offline, synchronizing only when a connection returns.
graph TD
A[Community Officer] -->|Input Data| B[React Native App]
A --> I[(SQL Server - DB)]
B -->|Write| C[(SQLite Local DB)]
C -->|Queue| D[Sync Manager]
D -->|Delta Sync via MQTT| E[API Gateway - Golang]
E -->|Publish| F[Apache Kafka]
F -->|Consumer| G[(MongoDB)]
F -->|Fan-out| H[Notification Service]
H -->|Push| I[Approval Dashboard]Engineering Decisions:
// Simplified Sync Consumer (Golang)
func (s *SyncService) ProcessSyncBatch(ctx context.Context, batch []SyncEvent) error {
for _, event := range batch {
existing, err := s.repo.FindByDeviceID(ctx, event.DeviceID, event.RecordID)
if err != nil {
return fmt.Errorf("failed to fetch record: %w", err)
}
if existing != nil && existing.UpdatedAt.After(event.Timestamp) {
// Server has newer data, send conflict notification
s.notifier.SendConflict(ctx, event.DeviceID, existing)
continue
}
if err := s.repo.Upsert(ctx, event.ToRecord()); err != nil {
return fmt.Errorf("failed to insert record: %w", err)
}
s.kafka.Publish("sync.completed", event)
}
return nil
}Results:
| Metric | Before | After | Improvement |
|---|---|---|---|
| Data loss rate | 23% | 0% | ↓ 100% |
| Avg sync time | 45 seconds | 3.2 seconds | ↓ 93% |
| Bandwidth usage per sync | 2.4 MB | 520 KB | ↓ 78% |
| App crash rate (offline) | 12% | 0.3% | ↓ 97.5% |
The Problem: The application form has 200+ fields that change based on the selected financing product and customer profile. Hardcoding these rules in the mobile app would require an app update for every policy change.
The Solution: Server-Driven Form Engine. The backend delivers the JSON Schema that dictates the UI layout, validation rules, and field dependencies.
// Form Schema Structure (simplified)
interface FormField {
id: string;
type: "text" | "number" | "select" | "photo" | "signature";
label: string;
rules: ValidationRule[];
dependencies?: {
field: string;
condition: "equals" | "gt" | "lt";
value: any;
action: "show" | "hide" | "require";
}[];
}Cross-Field Validation Examples:
jenis_usaha === "pertanian", then lama_usaha must be minimum 2 years.income_busy_day < angsuran * 3 will trigger a warning for the IIR limit.status_perkawinan === "menikah", then img_surat_nikah_url and img_ktp_pasangan_url become mandatory.Results:
| Metric | Before | After | Improvement |
|---|---|---|---|
| Form update cycle | 2-3 weeks (app release) | Instant Real-time | ↓ 100% |
| Incomplete submission | 34% | 4.2% | ↓ 88% |
| Avg complete form time | 45 minutes | 18 minutes | ↓ 60% |
The Problem: Financing approval involves multiple hierarchical stages. A delay at any stage creates a backlog for the entire branch.
The Solution: Event-driven architecture utilizing Kafka and AMQ to decouple the submission from the approval process, combined with a real-time tracking dashboard.
graph LR
CO[CO Submit] -->|Kafka Event| BM[Branch Manager]
BM -->|Approve/Reject| SBM[Sub-Branch Manager]
SBM -->|Approve/Reject| BC[Business Controller]
BC -->|Approve/Reject| DH1[Division Head 1]
DH1 -->|Approve/Reject via AMQ| DH2[Division Head 2 - Final]
DH2 -->|Final Decision| CORE[Core Banking System]Engineering Approach:
Results:
| Metric | Before | After | Improvement |
|---|---|---|---|
| Approval cycle time | 5-7 days | < 24 hours | ↓ 85% |
| Bottleneck detection | Manual | Real-time dashboard | — |
| Approval fraud rate | ~2.1% | 0.08% | ↓ 96% |
The Problem: Previously, field officers had to manually calculate the Installment to Income Ratio (IIR) using Excel templates or mental math, leading to frequent miscalculations and subsequent application rejections by the backend team.
The Solution: A real-time IIR computation API embedded directly within the dynamic form. As the officer inputs income and expenses, the engine recalculates eligibility limits instantaneously.
// Simplified IIR Simulation Model (Golang)
type IIRSimulation struct {
Tenor int `json:"tenor"`
Angsuran int64 `json:"angsuran"`
TotalIIR float64 `json:"total_iir"`
Margin float64 `json:"margin"`
IsEligible bool `json:"is_eligible"`
}
func SimulateIIR(totalIncome, existingInstallment, requestedAmount int64, tenors []int) []IIRSimulation {
var results []IIRSimulation
for _, tenor := range tenors {
angsuran := calculateAngsuran(requestedAmount, tenor, getMarginRate(tenor))
totalIIR := float64(angsuran+existingInstallment) / float64(totalIncome) * 100
results = append(results, IIRSimulation{
Tenor: tenor,
Angsuran: angsuran,
TotalIIR: totalIIR,
Margin: getMarginRate(tenor),
IsEligible: totalIIR <= 30.0, // OJK IIR threshold limit rule
})
}
return results
}Results:
| Metric | Before | After | Improvement |
|---|---|---|---|
| IIR Calculation Delay | 10+ Mins Manual Math | Instant 5ms API | ↓ 100% |
The Problem: A major challenge occurred during the Partial Rollout of the new Community Officer (CO) role. Previously, the lowest role was Business Manager (BM). When COs were introduced in v1.14.1, managers were still using older versions (v1.14.0 and v1.13.0) that did not recognize the "CO" entity. The older manager apps crashed when receiving submissions from COs because they lacked the UI components to render the new role data. A simultaneous update was impossible due to remote locations.
The Solution: I implemented a "Role-Based Downstream Adapter" in the Backend.
"Backward compatibility isn't just about keeping old data readable. The challenge is: How can past applications (Managers) still function when receiving data from the future (New Officers)?"
The overall implementation of Tepati fundamentally altered the operations of micro-financing:
After the MVP was successfully delivered, several non-technical challenges emerged that impacted future development velocity.
The initial architecture utilized AMQ and MQTT for handling offline-first connections. However, server infrastructure constraints caused the broker connection to drop intermittently. Post-launch, the protocol was migrated to HTTP-based to solve this instability and align with the mature enterprise backend ecosystem.
While MQTT is more bandwidth-efficient, maintaining a stateful connection broker at an enterprise scale requires high operational overhead. Shifting to HTTP guaranteed long-term maintainability by an operations team highly skilled in REST/HTTP environments, at the cost of slight bandwidth efficiency.
This is a lesson in "Organizational-Fit". The best architecture is not just the most technically advanced, but the one that can be well-supported by the organization's current operational capabilities.
A significant bottleneck during development iteration was the deployment pipeline speed.
| Stage | Duration | Notes |
|---|---|---|
| Build → Beta | ~15 minutes | Quite slow for rapid iteration |
| Build → Production | ~30 minutes | Excludes rollback if an error occurs |
| Hotfix cycle | 45-60 minutes | Includes re-test and re-deploy |
For comparison, the industry standard for modern pipelines is < 5 minutes for build-to-deploy. This slow pipeline hampered the feedback loop and delayed bug fixes in the field.
As the project matured, full enterprise security standards were enforced, including the use of standardized devices with EDR (Endpoint Detection & Response) and corporate VPNs. The challenge was balancing Compliance with Velocity:
This is a fair trade-off in banking: Security is non-negotiable—but the challenge is maintaining rapid feedback loops within those constraints.
Another key reflection is regarding engineer resource allocation in the maintenance phase.
1. Multitasking vs Deep Work In the initial phase (zero-to-one), a hybrid role (Backend + Infra + Support) is highly effective for speed. In the steady state phase, these roles need specialization. Expecting one engineer to handle business logic while dealing with infrastructure issues forces context switching, posing long-term quality risks.
Team efficiency isn't just measured by "how many roles one person handles", but by how focused the team can be on solving specific problems without excessive operational distractions.
2. Optimizing Processes Deployment processes and security policies are vital parts of governance. The challenge for engineering leadership is ensuring these processes are efficient (e.g., through CI/CD automation or policy whitelisting) so engineers construct solutions (build time) instead of waiting out processes (wait time).
3. Conclusion True efficiency occurs when support systems (people, process, tools) allow the engineering team to work with minimal friction, balancing rapid innovation with regulatory compliance.
Extreme early-stage speed can become addictive. However, allowing one person to hold the keys to the entire system without secondary support is a fatal managerial mistake.
The Illusion of Speed When you possess a "Hero Developer," you feel secure because features are delivered rapidly. The reality: you are accumulating compounding organizational debt. This dependency is a ticking time bomb.
Dependency on a single individual is the greatest Single Point of Failure (SPOF). If knowledge isn't distributed now, prepare for the bitter reality when a transition occurs:
Stop relying on one person. Break knowledge silos today, or you will be forced to reboot your product in the future.
Personal branding often depicts the "Single Fighter" as heroic. The reality is far more brutal.
I vividly remember Eid al-Fitr Eve (Malam Takbiran) last year. Traffic spiked drastically as officers chased targets before the long holiday. By 9:00 AM, there was an MQTT Broker Overload & Intermittent connection issue because millions of queued messages accumulated over flaky connections. While everyone else gathered with family, I sat in the guest room corner with my laptop, mutually restarting brokers and manually clearing retained messages via the terminal.
Building a system unilaterally means you forfeit the privilege to say "that's not my job description." Ownership is absolute. Even if the server crashes during your wedding ceremony, you are the one responsible for fixing it.
Beyond technical limitations, the greatest friction actually stems from the organizational side.
1. The "Service Center" Phenomenon My squad was frequently positioned as an escalation point for complex, immature features from other teams.
2. Silo Walls: Enterprise Security Integration A concrete example of friction was integrating the Enterprise Security Gateway in the On-Premise Infrastructure environment.
3. Hierarchical Gap The distance between "management" and "engineering" resembles an abyss. Decisions are frequently made in boardrooms absent context on the resultant technical debt, while engineers are too far removed from strategy to offer meaningful technical input.
This project demonstrates that a backend engineer with distributed systems architecture mastery — spanning API design, message brokers, to sync engines — can single-handedly deliver entire backend infrastructures within exceptionally tight timeframes.
The most difficult features aren't always advanced AI algorithms. Sometimes, engineering excellence revolves around empathy — understanding that your user is standing in a rural rice field with 1-bar signal, and ensuring the technology resiliently serves them effectively regardless.
What makes me proud: excellent technology is invisible. The officers don't need to know about Kafka, MQTT, or delta syncs. All they know is that this application just works, even deep in the forest — and I constructed the entire backend that supports it in 30 days.


