इजन
श्रेष्ठ
म पुरानो .NET लाई आधुनिक बनाउँछु र ५० प्रोजेक्टको बीमा प्लेटफर्म लाई जानीजानी शान्त राख्छु।
सोलुसन आर्किटेक्चरKubernetes मा बीमा प्लेटफर्म
हरेक सर्भिसका तीन replica र autoscaling, Postgres read replica, Redis, र outbox तथा saga सहितको RabbitMQ। pod मा ट्याप गरेर बन्द गर्नुहोस्, वा ट्रान्जेक्सन दोहोर्याउनुहोस्।
- .NET प्रोजेक्ट
- ५०
- Kubernetes deployment
- २८
- सकेट लीक सुधार
- ~३९० ठाउँमा
- लक्षित रनटाइम
- .NET 10
- C#
- ASP.NET Core
- EF Core
- Dapper
- Hangfire
- YARP
- Refit
- IdentityServer
- PostgreSQL
- SQL Server
- Redis
- Query tuning
- Migrations
- Docker
- Kubernetes
- Nginx
- Traefik
- Linux
- Let's Encrypt
- Serilog
- Seq
- Grafana Loki
- Correlation IDs
- Next.js
- React
- TypeScript
- Tailwind
- Strapi
मलाई भरोसा गरेर सुम्पिएका समस्याहरू।
नयाँ इन्जिनियरलाई ब्रिफ गरेजस्तै: के बिग्रियो, मैले के बदलेँ, र त्यसले के दियो।
केस ०१ / ०५
~३९०
new HttpClient() कल साइट हटाइए
- .NET
- HTTP
- Reliability
पूरै प्लेटफर्ममा सकेट सकिने समस्याको अन्त्य
- समस्या
- लोड बढ्दा बाहिर जाने कलहरू address-already-in-use त्रुटिसहित फेल हुन्थे, र सर्भिसहरूबीच टाइमआउट फैलिन्थ्यो।
- मैले के गरेँ
- समस्या TIME_WAIT मा अड्किएका करिब ३९०
new HttpClient()कल साइटसम्म पुर्याएँ, अनि तिनलाई नाम र टाइप भएका क्लाइन्ट तथा स्पष्ट टाइमआउटसहित IHttpClientFactory मा सारेँ। - नतिजा
- यो त्रुटि प्रोडक्सन लगबाट हरायो, र अब बाहिर जाने HTTP को retry र tracing एउटै ठाउँमा छ।
केस ०२ / ०५
१ ID
ले हरेक सर्भिसमा अनुरोध पछ्याउँछ
- Observability
- Serilog
- Loki
गेटवेदेखि डाटाबेससम्म एउटै ट्रेस
- समस्या
- हरेक सर्भिसले फरक तरिकाले लग गर्थ्यो, र .NET Core 2.2 का सर्भिसले काम लाग्ने लग लगभग लेख्दैनथे।
- मैले के गरेँ
- साझा लगिङ लाइब्रेरी (Serilog, Seq, Grafana Loki, ग्लोबल exception middleware) बनाएँ, र पुराना सर्भिसका लागि त्यसकै प्रतिरूप पनि, ताकि दुवै पुस्ताले एउटै ढाँचामा लग पठाऊन्।
- नतिजा
- अब एउटै correlation ID ले अनुरोधलाई सुरुदेखि अन्त्यसम्म पछ्याउँछ।
केस ०३ / ०५
६१%
प्रोजेक्ट सपोर्ट नभएको .NET मा थिए
- Architecture
- Migration
सपोर्ट सकिएको .NET बाट .NET 10 सम्म
- समस्या
- धेरैजसो प्रोजेक्ट .NET Core 2.2, 3.1 वा 5 मा थिए, र कुन प्रोजेक्टले बाँकीलाई रोकेको छ भन्ने कसैलाई थाहा थिएन।
- मैले के गरेँ
- हरेक प्रोजेक्टको अडिट गरेँ, IdentityServer4 लाई मुख्य अवरोधका रूपमा पहिचान गरेँ, र .NET 10 सम्मको सीधा बाटो योजना बनाएँ।
- नतिजा
- थाहा भएका जोखिमसहितको क्रमबद्ध रोडम्याप; पहिलो सर्भिसहरू .NET 10 मा चलिसकेका छन्।
केस ०४ / ०५
v16
Next.js, साथमा React 19, Strapi 5 र Node 24
- Next.js
- Strapi
- Release
Next.js 16 मा सार्वजनिक वेबसाइट
- समस्या
- साइट र CMS सपोर्ट सकिएका र ज्ञात कमजोरी भएका संस्करणमा थिए।
- मैले के गरेँ
- फ्रन्टएन्ड, CMS र मेल सर्भिस एकैसाथ अपग्रेड गरेँ, लाइभ जानुअघि नै rollback किट तयार राखेर।
- नतिजा
- जाँचिएको rollback बाटोसहित प्रोडक्सनमा पुग्यो।
केस ०५ / ०५
४
स्थायी ब्रान्च, एउटै promotion बाटो
- Process
- Git
- Leadership
टिमले साँच्चै पालना गर्ने रिलिज मापदण्ड
- समस्या
- Hotfix हरू dev मा नपुगी prod मा पुग्थे, र UAT शिप भएको कोडभन्दा फरक हुँदै जान्थ्यो।
- मैले के गरेँ
- स्थायी main, dev, uat र prod ब्रान्च र स्पष्ट promotion बाटो लेखेर लागू गरेँ।
- नतिजा
- रिलिजहरू अब तालिका बनाउन सकिने गरी अनुमानयोग्य छन्।
१८४ मिलिसेकेन्ड, सात तह।
हरेक अनुरोध प्रोक्सी, गेटवे, सर्भिस, HTTP क्लाइन्ट, डाटाबेस र लगरबाट गुज्रन्छ। मैले यी हरेकलाई बिगारेको र सुधारेको छु, र सिकेका कुरा लेखेको छु। कुनै एक span छान्नुहोस्।
POST/api/policies/quote
trace 7f3a…c91e, 200 OK
0ms
मैले चलाउने स्ट्याकबाट गुज्रिएको एउटा अनुरोध। हरेक span मैले बनाएको, बिगारेको वा सुधारेको तह हो। त्यसबारेको लेख पढ्न कुनै एक छान्नुहोस्।
Controller देखि architecture सम्म, अनि फेरि फर्केर।
एउटै feature, पोलिसी जारी गर्ने, इन्जिनियरले पहिलो वर्षदेखि आर्किटेक्टको कुर्सीसम्म लेख्ने तरिकामा। स्क्रोल गर्दै हेर्नुहोस्।
०१/०७जुनियर
पहिले चलाऊ।
सबै कुरा controller मै: SQL, व्यापार नियम, इमेल, सबै। मेरो मेसिनमा चल्छ, किन चल्छ कसैलाई थाहा छैन।
Controllers/PolicyController.cs[HttpPost("save")] public IActionResult Save(PolicyDto dto) { var conn = new SqlConnection("Server=prod;User=sa;Password=123"); conn.Open(); var sql = "INSERT INTO Policy VALUES ('" + dto.Name + "', " + dto.Premium + ")"; new SqlCommand(sql, conn).ExecuteNonQuery(); if (dto.Premium > 100000) new SmtpClient("mail").Send("boss@co", "Big policy!", dto.Name, ""); return Ok("saved"); // works on my machine }चरण ०१/०७०२/०७मिड-लेभल
तह छुट्याऊ।
Controller पातलो हुन्छ, service तह आउँछ, र पहिलो unit test देखिन्छन्। धेरैजसो mock ले mock फर्काउँछ कि भनेर जाँच्ने।
Services/PolicyService.cspublic class PolicyService : IPolicyService { private readonly IPolicyRepository _repo; private readonly IEmailService _email; public PolicyService(IPolicyRepository repo, IEmailService email) => (_repo, _email) = (repo, email); public async Task<int> CreateAsync(PolicyDto dto) { var id = await _repo.AddAsync(dto.ToEntity()); if (dto.Premium > 100_000) await _email.NotifyUnderwriterAsync(id); return id; } }चरण ०२/०७०३/०७सिनियर
सही बनाऊ।
Red, green, refactor। test पहिले आउँछ, र SOLID अन्तर्वार्ताको उत्तर मात्र नभई कोडको आकार बन्छ।
SOLID, बीमाका उदाहरणसहित
Single responsibility
परिवर्तनको एउटै कारण। प्रिमियम हिसाबलाई इमेलबारे थाहा हुँदैन।
class PremiumCalculator { Money Calculate(Risk risk) { /* … */ } } class UnderwriterNotifier { Task Notify(PolicyId id) { /* … */ } }Tests/IssuePolicyTests.cs// Red: written before Policy.Issue exists [Fact] public void Issuing_above_the_limit_needs_underwriting() { var policy = Policy.Quote(Premium.Of(250_000m, "NPR")); var result = policy.Issue(Limits.Default); result.Should().BeOfType<NeedsUnderwriting>(); policy.Status.Should().Be(PolicyStatus.Referred); } // Green: the smallest Issue() that passes. // Refactor: now the rest of the design can move safely.चरण ०३/०७०४/०७टेक लिड
टिकाउ बनाऊ।
हरेक module भित्र clean architecture, बाहिर modular monolith। सीमाहरू code review मा बहस हुनुअघि ADR मा तय हुन्छन्।
Dependency भित्रतिर देखाउँछन्। डोमेनलाई EF Core वा HTTP बारे केही थाहा हुँदैन। कोड हेर्न कुनै ring छान्नुहोस्।
Domain/Policy.cs
public sealed class Policy : AggregateRoot { public PolicyStatus Status { get; private set; } = PolicyStatus.Quoted; public Money Premium { get; private set; } public Result Issue(Limits limits) { if (Premium > limits.AutoIssue) return Refer("needs underwriting"); Status = PolicyStatus.Issued; Raise(new PolicyIssued(Id, Premium)); return Result.Ok(); } }चरण ०४/०७०५/०७आर्किटेक्ट
स्केल गर।
टिम वा लोडले माग्दा मात्र module सर्भिस बन्छ, त्यसअघि होइन। RabbitMQ मा सन्देश, भरोसाका लागि outbox, distributed transaction को सट्टा saga।
Reinsurance/PolicyIssuedConsumer.cspublic sealed class PolicyIssuedConsumer(ITreaties treaties, IPublishEndpoint bus) : IConsumer<PolicyIssued> { public async Task Consume(ConsumeContext<PolicyIssued> ctx) { var msg = ctx.Message; var cession = await treaties.Cede(msg.PolicyId, msg.Premium); // No distributed lock: each service commits locally, the saga reacts await (cession.IsRejected ? bus.Publish(new CessionRejected(msg.PolicyId)) : bus.Publish(new PolicyCeded(msg.PolicyId, cession.Share))); } }चरण ०५/०७०६/०७सोलुसन आर्किटेक्ट
निर्भर गर्छ।
हरेक उत्तर trade-off तालिकाबाट सुरु हुन्छ। यो पेजको माथिको सोलुसन आर्किटेक्चर नै काम हो: हरेक बक्स एउटा निर्णय, हरेक तीर एउटा trade-off।
docs/adr/0007-cross-service-writes.md# ADR 0007: Cross-service writes Status: accepted | Option | Consistency | Ops cost | Team fit | |----------------------|-------------|----------|----------| | Distributed tx (2PC) | strong | high | poor | | Outbox + saga | eventual | medium | good | | Shared database | strong | low | coupling | Decision: outbox + saga over RabbitMQ. Consequence: every consumer is idempotent (Redis keys).चरण ०६/०७०७/०७शुक्रबार, ४:५५ बजे
अनि डेडलाइन आयो।
दस वर्षको TDD, DDD, clean architecture र microservices पछि पनि hotfix फेरि controller मै शिप हुन्छ।
Controllers/PolicyController.csprod मा चल्छ[HttpPost("hotfix")] public async Task<IActionResult> Fix() { // TODO: move this into the domain after the release await _db.Database.ExecuteSqlRawAsync( "UPDATE Policy SET Status = 'Issued' WHERE Id = 4021"); return Ok("fixed"); // it's Friday }चरण ०७/०७
[HttpPost("save")]
public IActionResult Save(PolicyDto dto)
{
var conn = new SqlConnection("Server=prod;User=sa;Password=123");
conn.Open();
var sql = "INSERT INTO Policy VALUES ('" + dto.Name + "', " + dto.Premium + ")";
new SqlCommand(sql, conn).ExecuteNonQuery();
if (dto.Premium > 100000)
new SmtpClient("mail").Send("boss@co", "Big policy!", dto.Name, "");
return Ok("saved"); // works on my machine
}डाटा डेस्कदेखि प्लेटफर्म सम्हाल्नेसम्म।
कम्प्युटर साइन्स एन्ड इन्फरमेसन टेक्नोलोजीमा BSc
वेदास कलेज, २०१७ – २०२२
जनवरी २०२५ – हालसम्मअहिले
सिनियर सफ्टवेयर इन्जिनियर
Himalayan Everest Insurance, काठमाडौं, हाइब्रिड
- लेखा, दाबी, पुनर्बीमा र रिपोर्टिङ समेटिएको, २८ Kubernetes workload मा चल्ने ५० .NET प्रोजेक्टको बीमा प्लेटफर्मको ब्याकएन्ड विश्वसनीयता र आधुनिकीकरणको जिम्मा।
- .NET Core 2.2, 3.1 र 5 बाट .NET 10 सम्मको अपग्रेडको नेतृत्व, IdentityServer4 को विकल्पसहित।
- सबै सर्भिसमा structured logging र tracing बनाएँ।
- सार्वजनिक वेबसाइट र CMS लाई Next.js 16, React 19, Strapi 5 र Node 24 मा अपग्रेड गरेँ।
जुलाई २०२२ – नोभेम्बर २०२४
.NET डेभलपर
EKbana Solutions, ललितपुर, अन-साइट
- ASP.NET Core, PostgreSQL, EF Core र Dapper प्रयोग गरेर इन्टरप्राइज बीमा कार्यप्रवाहका ब्याकएन्ड सर्भिस डिजाइन गरेँ।
- ग्राहक पोर्टल, भुक्तानी र बाह्य पार्टनरका लागि REST API र इन्टिग्रेसन बनाएँ।
- Idempotency, retry र failure recovery सहित Hangfire मा ब्याकग्राउन्ड कार्यप्रवाह चलाएँ।
- Docker, Linux, Nginx/Traefik र Kubernetes मा शिप गरेँ; IdentityServer, JWT र RBAC मा काम गरेँ।
जनवरी २०२२ – जुन २०२२
डटनेट डेभलपर
NBIT, ललितपुर
- ग्राहक प्रोजेक्टका लागि .NET Framework एप्लिकेसन बनाएँ र सम्हालेँ।
नोभेम्बर २०१७ – जुन २०२२
डाटा स्पेसियलिस्ट
CloudFactory, काठमाडौं
- डिग्री पढ्दै गर्दा अन्तर्राष्ट्रिय ग्राहकका लागि उच्च शुद्धताको डाटा काम गरेँ।
प्रोडक्सनबाट टिपोटहरू।
.NET, इन्फ्रास्ट्रक्चर र टिम चलाउनेबारे १४ वटा लेख। धेरैजसो कुनै घटनाबाट सुरु भएका हुन्। लेखहरू अंग्रेजीमा छन्।
कुनै प्रणालीलाई स्थिर हात चाहिएको छ?
ब्याकएन्ड आर्किटेक्चर र .NET आधुनिकीकरणमा सिनियर तथा लिड भूमिकाका लागि उपलब्ध छु, काठमाडौंमा वा रिमोट।
काठमाडौंको स्थानीय समय: KTM, UTC+5:45
➜ ~ whoami
Ejan Shrestha, सिनियर सफ्टवेयर इन्जिनियर, काठमाडौं, नेपाल
➜ ~ contact --email
➜ ~