<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://raghu.app/</id>
    <title>Raghunandhan VR</title>
    <updated>2026-04-10T12:40:23.155Z</updated>
    <generator>Feed for Node.js</generator>
    <author>
        <name>Raghunandhan VR</name>
        <email>raghunandhanvr@outlook.com</email>
        <uri>https://raghu.app/</uri>
    </author>
    <link rel="alternate" href="https://raghu.app/"/>
    <link rel="self" href="https://raghu.app/atom.xml"/>
    <subtitle>I own a computer and I like to develop things with it.</subtitle>
    <logo>https://raghu.app//opengraph-image.png</logo>
    <icon>https://raghu.app/favicon.ico</icon>
    <rights>All rights reserved 2026, Raghunandhan VR</rights>
    <entry>
        <title type="html"><![CDATA[munnar]]></title>
        <id>https://raghu.app/writings/munnar</id>
        <link href="https://raghu.app/writings/munnar"/>
        <updated>2026-04-10T12:40:23.150Z</updated>
        <content type="html"><![CDATA[import { WritingsArticleLayout } from '@/app/components/writings/writings-article-layout'
import { BlogViewCounter } from '@/app/components/ui/blog-view-counter'

export const article = {
  title: 'Munnar',
  description: 'Just a few tips for exploring Munnar',
}

export const metadata = {
  title: 'Munnar',
  description: article.description,
  alternates: {
    canonical: '/writings/munnar',
  },
  openGraph: {
    images: [
      {
        url: `/api/og?title=Munnar`,
        width: 1200,
        height: 630,
      },
    ],
  },
};

<WritingsArticleLayout title={article.title} description={article.description} meta={<BlogViewCounter slug='/writings/munnar' createdAt={new Date('2025-09-27')} />}>

I've explored most of the mountains in South India. Munnar is simply the best, no question. Munnar is the best.

Still for those who want a true deep forest experience, go to [Gavi](https://www.google.com/search?q=Gavi+Forest). Now, let's talk about Munnar.

Being from a village near Udumalpet, I'm less than 60km from Munnar, so I've spent countless weekends exploring these hills. 

Munnar consists of 4 major road directions from the town:
- One towards **Udumalpet** (my home route)
- One towards **Vattavada** (most popular tourist route)
- One towards **Theni** (Bodi and Kumili in Poppara junction)
- Another towards **Cochin** (fully damaged and nasty as of 2025)

All roads except Cochin are fantastic for travel.

## Vattavada Road (Common tourist spots, the Traffic is always there)

These places are for **families with big crowds**, and this road will be in traffic most of the time until Top Station. Perfect for **first-time visitors** who want the typical Munnar experience.

1. [Boomer uncle tourist spots](https://www.google.com/search?q=Munnar+botanical+garden+elephant+safari) like botanical garden, elephant safari. Just skip them.
2. [Top Station](https://www.google.com/search?q=Top+Station+Munnar) - The highest point, great views but always crowded
3. [Vattavada](https://www.google.com/search?q=Vattavada+Munnar) - Small village with organic farms, less touristy
4. [Dams on the way](https://www.google.com/search?q=Munnar+dams) - Good photo spots, especially during monsoon
5. [Tea estates on the road](https://www.google.com/search?q=Munnar+tea+estates) - Multiple stops for tea tasting

## Theni Road (Mostly Trekking)

I highly recommend this for **trekking enthusiasts**. This route takes you through some of the most **challenging and rewarding treks** in the region.

1. [Chokramudi Trek](https://www.google.com/search?q=Chokramudi+Trek+Munnar) - Moderate difficulty, 3-4 hours, amazing sunrise views
2. [Kollukumalai Trek](https://www.google.com/search?q=Kollukumalai+Trek+Munnar) - Starts at 5am, need to reach before sunrise, but worth it
3. [Mesapulimala Trek](https://www.google.com/search?q=Mesapulimala+Trek+Munnar) - One of the toughest, 6-8 hours, for experienced trekkers
4. [Tea estates on the road](https://www.google.com/search?q=Theni+road+tea+estates+Munnar) - Less crowded than Vattavada route, better for photography

## Udumalpet Road

This is **my home route** - less touristy, more authentic. Perfect for those who want to experience the **real Munnar without the crowds**.

1. [Thoovanam waterfalls Trek](https://www.google.com/search?q=Thoovanam+waterfalls+Trek+Munnar) - 2-3 hours, beautiful waterfall, good for families
2. [Kanthaloor half day visit](https://www.google.com/search?q=Kanthaloor+Munnar) - Apple orchards and spice gardens, unique to this area
3. [Anamudi](https://www.google.com/search?q=Anamudi+Munnar) - South India's highest peak, restricted access but worth the effort
4. [Chinnar forest safari](https://www.google.com/search?q=Chinnar+forest+safari+Munnar) - Wildlife spotting, best during early morning or late evening
5. [Tea estates on the road](https://www.google.com/search?q=Udumalpet+road+tea+estates+Munnar) - Working plantations, not just tourist spots

## Hostels

1. [Zostel](https://www.google.com/search?q=Zostel+Munnar)
2. [The hostellers](https://www.google.com/search?q=The+hostellers+Munnar)

## Campsites

1. [Cox Cargill](https://www.google.com/search?q=Cox+Cargill+Munnar)
2. [Cloud Farm Munnar](https://www.google.com/search?q=Cloud+Farm+Munnar)
3. [Outernest campers](https://www.google.com/search?q=Outernest+campers+Munnar)
4. [Magic valley](https://www.google.com/search?q=Magic+valley+Munnar)
5. [Wild sherpas tenting and camping](https://www.google.com/search?q=Wild+sherpas+Munnar)

## Decent stays for family

1. [Tea county](https://www.google.com/search?q=Tea+county+Munnar)
2. [The Fog Munnar](https://www.google.com/search?q=The+Fog+Munnar)
3. [Thrill Holiday](https://www.google.com/search?q=Thrill+Holiday+Munnar)
4. [K Mansion](https://www.google.com/search?q=K+Mansion+Munnar)
5. [Cloud castle resorts](https://www.google.com/search?q=Cloud+castle+resorts+Munnar)

## Luxury stays

1. [Eden woods resort](https://www.google.com/search?q=Eden+woods+resort+Munnar)
2. [Elephant passage](https://www.google.com/search?q=Elephant+passage+Munnar)
3. [Ragamaya resort](https://www.google.com/search?q=Ragamaya+resort+Munnar)
4. [The panoramic getaway](https://www.google.com/search?q=The+panoramic+getaway+Munnar)

## Best Airbnbs

1. [Mudhouse marayoo](https://www.google.com/search?q=Mudhouse+marayoo+Munnar)
2. [Footprint - wilderness experience](https://www.google.com/search?q=Footprint+Munnar+wilderness)

## Peak Male Experience (5 Days)

**5 days for guys who want to feel themselves 100% in the forest:**

- **Day 1**: **Deep in the forest and camp**, one day inside the forest, no phone, no internet, no distractions, just you and the forest
- **Day 2**: **Hidden waterfall trek** through dense forests, ending at a crystal clear natural pool
- **Day 3**: **Off-road biking** to mountain peaks, just pure one day adventure, peak male experience
- **Day 4**: **Volunteering in Vattavada** - work with locals
- **Day 5**: **Night camping** under starry skies at a secluded mountain peak

*This will make you feel the forest and you.* 

Contact me at **+91 8667322394**.

Need still more of adventure? Let's talk about [Gavi](https://www.google.com/search?q=Gavi).

## Self Drive Bikes

1. [Gokulam](https://www.google.com/search?q=Gokulam+Munnar+bike+rental): +91 9447237165
2. [Shalom](https://www.google.com/search?q=Shalom+Munnar+bike+rental): +91 8281792798
3. [MBR](https://www.google.com/search?q=MBR+Munnar+bike+rental): +91 9447303119
4. [Sangeetha](https://www.google.com/search?q=Sangeetha+Munnar+bike+rental): +91 9447220648

## Self Drive Cars

1. [Royalpicks](https://www.google.com/search?q=Royalpicks+Munnar+car+rental): +91 9629926888

## Now the below items are for those who are new to Munnar

## Boys Trip (New to Munnar)

**4 days with trekking, volunteering, and authentic experiences for first-time visitors:**

- **Day 1**: Rent 300cc+ bike, explore **Vattavada route** early morning, stay in campsites (40km from Munnar)
- **Day 2**: Return from Vattavada, head to **Udumalpet road**, stay in Marayoor, explore Kanthaloor road (60km from Munnar)
- **Day 3**: Start at 5am for **Kollukumalai trek** (reach before sunrise), return and do **Chokramudi trek** based on energy (both on Theni road, 30km from Munnar)
- **Day 4**: **Thoovanam falls** and **Chinnar safari**, volunteer in Vattavada, explore remaining spots

## Family Trip Plans

### 3-Day Family Plan (No Trekking)

- **Day 1**: Vattavada route - Top Station, tea estates, dams, botanical garden
- **Day 2**: Udumalpet road - Kanthaloor, Thoovanam falls, Chinnar safari
- **Day 3**: Munnar town - Tea museum, local markets, relaxation

### 3-Day Family Plan (Light Trekking)

- **Day 1**: Vattavada route + easy trek to Top Station
- **Day 2**: Udumalpet road - Kanthaloor, Thoovanam falls (easy trek), Chinnar safari
- **Day 3**: Theni road - Chokramudi trek (moderate), tea estates

### 3-Day Family Plan (With Elderly)

- **Day 1**: Munnar town - Tea museum, local sightseeing, botanical garden
- **Day 2**: Vattavada route - Dams, tea estates (no trekking), comfortable stays
- **Day 3**: Udumalpet road - Kanthaloor (by car), Chinnar safari (comfortable vehicle)

</WritingsArticleLayout>
]]></content>
        <author>
            <name>Raghunandhan VR</name>
            <email>raghunandhanvr@outlook.com</email>
            <uri>https://raghu.app/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[dx]]></title>
        <id>https://raghu.app/writings/dx</id>
        <link href="https://raghu.app/writings/dx"/>
        <updated>2026-04-10T12:40:23.150Z</updated>
        <content type="html"><![CDATA[import { WritingsArticleLayout } from '@/app/components/writings/writings-article-layout'
import { BlogViewCounter } from '@/app/components/ui/blog-view-counter'

export const article = {
  title: 'On Developer Experience',
  description: 'Some thoughts on what makes a great DX',
}

export const metadata = {
  title: 'On Developer Experience',
  description: article.description,
  alternates: {
    canonical: '/writings/dx',
  },
  openGraph: {
    images: [
      {
        url: `/api/og?title=On+Developer+Experience`,
        width: 1200,
        height: 630,
      },
    ],
  },
};

<WritingsArticleLayout title={article.title} description={article.description} meta={<BlogViewCounter slug='/writings/dx' createdAt={new Date('2025-01-24')} />}>

My perspective on what creates a good developer experience:

## Shipping

- Stop talking, start shipping—just get it out
- We’re only as good as our last release

## Collaboration

- Why waste time in meetings when pair programming gets things done?
- Clear, direct chats beat long email threads

## Intensity

- Think big—actually do it
- If something feels off, say it
- Don’t shy away from tough questions

## Craftsmanship

- Details matter—take full ownership of what you build
- Leave the code cleaner than you found it, but only if you’re working on that part

## Autonomy

- Figure out your role, then own it
- If you’re stuck, ask for help early and move on

## Humility

- It’s fine to be wrong—just learn and move forward
- Confidence is good; arrogance isn’t
- Be open to others’ ideas, even if you’re sure you’re right

</WritingsArticleLayout>
]]></content>
        <author>
            <name>Raghunandhan VR</name>
            <email>raghunandhanvr@outlook.com</email>
            <uri>https://raghu.app/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[deletion-focused]]></title>
        <id>https://raghu.app/writings/deletion-focused</id>
        <link href="https://raghu.app/writings/deletion-focused"/>
        <updated>2026-04-10T12:40:23.149Z</updated>
        <content type="html"><![CDATA[import { WritingsArticleLayout } from '@/app/components/writings/writings-article-layout'
import { BlogViewCounter } from '@/app/components/ui/blog-view-counter'

export const article = {
  title: 'Code for Deletion, Not Reuse',
  description: 'Good code isn\'t reusable; it\'s deletable. Every abstraction is a bet on future requirements you\'ll probably get wrong.',
}

export const metadata = {
  title: 'Code for Deletion, Not Reuse',
  description: article.description,
  alternates: {
    canonical: '/writings/deletion-focused',
  },
  openGraph: {
    images: [
      {
        url: `/api/og?title=Code+for+Deletion%2C+Not+Reuse`,
        width: 1200,
        height: 630,
      },
    ],
  },
};

<WritingsArticleLayout title={article.title} description={article.description} meta={<BlogViewCounter slug="/writings/deletion-focused" createdAt={new Date('2025-07-29')} />}>

**TL;DR**: Good code isn't **reusable**; it's **deletable**. Every abstraction is a bet on future requirements you'll probably get wrong. Write code that's **easy to throw away**.

## Why?

Software requirements **change faster than we can predict them**. The code you write today solving tomorrow's imagined problems becomes the **legacy nightmare you can't remove** next year.

**Key Insights**:
- **Duplication is cheaper than the wrong abstraction**
- Tight coupling makes change expensive. **Optimize for disposal** instead
- **Maintenance cost > Writing cost**
- Ask **"How do I delete this?"** not "How do I reuse this?"

## The Reusability Trap

Every shared abstraction creates a **web of dependencies**. The more code that depends on your "reusable" component, the more expensive it becomes to change until it's **effectively frozen forever**.

```go
// What starts as "reusable"
type AuthService interface {
    Authenticate(ctx context.Context, opts ...AuthOption) (*User, error)
}

// Becomes unmaintainable
func (a *AuthManager) Authenticate(ctx context.Context, opts ...AuthOption) (*User, error) {
    // 47 microservices depend on this
    // 15 auth providers
    // 500 lines of edge cases
    // Can't change without breaking everything
}
```

**Every consumer multiplies the cost of change.**

## But What About DRY?

**The Counterargument**: "Don't Repeat Yourself is a fundamental principle! Duplication leads to bugs when you update one place but forget another. We should abstract early to prevent inconsistencies."

**The Reality**: DRY prevents one type of bug but creates another: **premature abstraction**. Yes, duplication can cause sync issues, but **wrong abstractions cause architectural paralysis**. A duplicated bug is annoying. A wrong abstraction touching 50 files is **a crisis**.

```go
// The DRY advocate's dream
type DataProcessor interface {
    Process(data interface{}) (interface{}, error)
}

// The reality: each implementation fights the abstraction
func (p *JSONProcessor) Process(data interface{}) (interface{}, error) {
    // 40 lines of type assertions and special cases
}
```

## Copy-Paste Driven Development

Duplication lets you understand patterns through **experience, not speculation**. It's easier to extract the right abstraction from **three examples** than to guess it from one.

```go
// Monday: Stripe handler
func handleStripe(amount int64, token string) error {
    return stripe.Charge(amount, token)
}

// Tuesday: PayPal handler (copied)
func handlePayPal(amount int64, token string) error {
    return paypal.Process(amount, token)
}

// Friday: Pattern emerges naturally
type PaymentHandler interface {
    Process(amount int64, token string) error
}
```

**Abstraction after repetition, not before.**

## "But This Doesn't Scale!"

**The Counterargument**: "In large organizations, we need shared libraries and consistent patterns. Without reusable components, every team reinvents the wheel. This leads to chaos."

**The Reality**: Shared libraries become **dependency nightmares**. That "standard" authentication library? Now you need **6 months and 12 teams** to agree on any change. Meanwhile, teams work around it, creating the **very inconsistency you tried to avoid**.

```go
// The "shared" library everyone depends on
import "company/shared/auth" // version locked since 2019

// What teams actually do
func authenticateUser(token string) (*User, error) {
    // Call the shared library
    user, err := auth.Validate(token)
    
    // Then work around its limitations
    if user.Type == "special_case_shared_lib_doesnt_handle" {
        // 50 lines of workarounds
    }
}
```

## Layer by Volatility

**Business logic changes constantly. Infrastructure rarely does.** Keep them separate: **volatile code at the top, stable code at the bottom**. This way, you **mostly delete from the top**.

```
┌─────────────────┐
│ Business Logic  │ ← Changes daily (easy to delete)
├─────────────────┤
│ Domain Models   │ ← Changes monthly
├─────────────────┤
│ HTTP/Database   │ ← Changes yearly (hard to delete)
└─────────────────┘
```

## "What About Code Reviews?"

**The Counterargument**: "Duplicated code makes reviews harder. Reviewers see the same logic repeatedly. Abstractions make code more readable and reviewable."

**The Reality**: Reviewing a **wrong abstraction is worse than reviewing duplication**. With duplication, you **see exactly what code does**. With a bad abstraction, you chase through **layers of indirection** wondering why `AbstractFactoryBuilderStrategy` exists.

```go
// Easy to review (even if duplicated)
func calculateTax(amount float64) float64 {
    return amount * 0.08
}

// "Abstracted" version
func calculateTax(amount float64) float64 {
    return getTaxStrategy().
        withRegion(getRegion()).
        withRules(loadRules()).
        calculate(amount)
}
```

**Which would you rather debug at 3 AM?**

## The Deletion Checklist

Before writing code, ask:
- **Can I delete this without touching other files?**
- **What breaks when I remove this?**
- Is this solving **today's problem** or **tomorrow's maybe-problem**?
- Am I creating an abstraction from **actual patterns** or **imagined ones**?

## The Hard Truth

Yes, this approach has tradeoffs. Yes, you'll have some duplication. Yes, it goes against what you learned in CS class. But here's what the DRY advocates won't tell you: **most production codebases are haunted by abstractions someone created in 2015 that nobody can remove**.

Remember: **Every abstraction is a bet on the future, and the house always wins**. Today's "perfect" reusable component is tomorrow's **legacy bottleneck that three teams are afraid to touch**. Write code that **admits it might be wrong**. Build systems that can evolve by **subtraction, not just addition**. Because in the end, the code that survives isn't the code that does everything. **It's the code that can gracefully disappear when its time is up**. 

</WritingsArticleLayout>]]></content>
        <author>
            <name>Raghunandhan VR</name>
            <email>raghunandhanvr@outlook.com</email>
            <uri>https://raghu.app/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[skipper]]></title>
        <id>https://raghu.app/writings/skipper</id>
        <link href="https://raghu.app/writings/skipper"/>
        <updated>2026-04-10T12:40:23.149Z</updated>
        <content type="html"><![CDATA[import MermaidDiagram from '@/app/components/md/mermaid'
import { WritingsArticleLayout } from '@/app/components/writings/writings-article-layout'
import { BlogViewCounter } from '@/app/components/ui/blog-view-counter'

export const article = {
  title: 'High-Performance Reverse Proxy',
  description: 'About Zalando Skipper architecture, performance optimizations, and unique features.',
}

export const metadata = {
  title: 'High-Performance Reverse Proxy',
  description: article.description,
  alternates: {
    canonical: '/writings/skipper',
  },
  openGraph: {
    images: [
      {
        url: `/api/og?title=High-Performance+Reverse+Proxy`,
        width: 1200,
        height: 630,
      },
    ],
  },
};

<WritingsArticleLayout title={article.title} description={article.description} meta={<BlogViewCounter slug="/writings/skipper" createdAt={new Date('2025-02-02')} />}>

We recently switched to [Zalando Skipper](https://github.com/zalando/skipper) for our reverse proxy needs after trying out Nginx, HAProxy, Traefik, etc,.

I'm writing this blog because I spent weeks evaluating different reverse proxies for our infrastructure, and Skipper's approach to solving common proxy challenges really stood out. While digging through their source code, I found several interesting implementation details that I think are worth sharing - especially if you're building networked services in Go. 

Looking at their codebase, PRs, it's clear that Zalando properly follows the principles of good [dx](/writings/dx) - the code is well-structured, thoroughly documented, and designed with extensibility in mind. Whether you're evaluating reverse proxies or just interested in high-performance Go code, there's a lot to learn from their approach.

Skipper's Go-based architecture, extensibility, and performance in high-concurrency scenarios made it stand out. I've been learning from their codebase on how they do memeory management, goroutine handling, and routing.

<MermaidDiagram diagram={`
graph TD
    A[Incoming Request] --> B[Parsing]
    B --> C{Routing}
    C -->|Match| D[Filters]
    C -.->|No Match| Z[404]
    D --> E[Backend]
    E --> F[Response Processing]
    F --> G[Response to Client]

    H[Memory Management] -.-> B
    H -.-> E
    I[Concurrency Model] -.-> C
    I -.-> D
    J[Routing Engine] -.-> C
    K[Load Balancing] -.-> E
    L[Observability] -.-> F
`} />

## 1. Memory Management and Concurrency

Skipper manages memory pretty efficiently. Here are some interesting techniques they use:

### Object Pooling

They're using object pooling for request contexts. Here's a simplified version:

```go
import (
    "net/http"
    "sync"
    "time"
)

type proxyContext struct {
    Request        *http.Request
    Response       http.ResponseWriter
    roundTripStart time.Time
    filters        []filters.Filter
}

var proxyContextPool = sync.Pool{
    New: func() interface{} {
        return &proxyContext{
            filters: make([]filters.Filter, 0, 10),
        }
    },
}

func acquireContext(w http.ResponseWriter, r *http.Request) *proxyContext {
    ctx := proxyContextPool.Get().(*proxyContext)
    ctx.reset(w, r)
    return ctx
}

func releaseContext(ctx *proxyContext) {
    ctx.Request = nil
    ctx.Response = nil
    proxyContextPool.Put(ctx)
}
```

What's cool here is they pre-allocate the `filters` slice. This avoids allocations during request processing, which is crucial for low latency under high load. The time complexity for acquiring and releasing contexts is O(1), which is great for performance.

### Request Handling

Here's how Skipper handles requests:

```go
func (p *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    ctx := acquireContext(w, r)
    defer releaseContext(ctx)

    if err := p.process(ctx); err != nil {
        p.errorHandler(ctx, err)
    }
}
```

This setup allows Skipper to handle a large number of concurrent connections efficiently. Each request gets its own goroutine, leveraging Go's concurrency model.

## 2. Filter Chain Execution

Skipper's filter chain is both flexible and fast. Here's how they process it:

```go
func (p *proxy) process(ctx *proxyContext) error {
    for _, f := range ctx.filters {
        if err := f.Request(ctx); err != nil {
            return err
        }
    }

    if err := p.forward(ctx); err != nil {
        return err
    }

    for i := len(ctx.filters) - 1; i >= 0; i-- {
        if err := ctx.filters[i].Response(ctx); err != nil {
            return err
        }
    }

    return nil
}
```

The time complexity here is O(n), where n is the number of filters. But what's clever is that by keeping the filter functions small and simple, they're allowing the Go compiler to inline these calls, reducing function call overhead.

I'm still exploring their filter implementations, but I've been working on a custom rate limiting filter. Below's just a very basic example:

```go
type rateLimitFilter struct {
    limit rate.Limit
    burst int
    limiter *rate.Limiter
}

func (f *rateLimitFilter) Request(ctx filters.FilterContext) {
    if !f.limiter.Allow() {
        ctx.Serve(&http.Response{
            StatusCode: http.StatusTooManyRequests,
            Body:       ioutil.NopCloser(strings.NewReader("Rate limit exceeded")),
        })
    }
}
```

This filter integrates seamlessly with Skipper's existing chain. I'm impressed by how easy it is to extend Skipper's functionality.

## 3. Routing and Load Balancing

Skipper's routing engine is pretty smart. They use a trie for static routes and regex for dynamic ones.

### Trie-based Route Matching

Here's a simplified version of their trie-based route matching:

```go
type trieNode struct {
    children map[string]*trieNode
    route    *Route
}

func (t *trie) lookup(path string) *Route {
    node := t.root
    segments := strings.Split(path, "/")
    for _, segment := range segments {
        child, exists := node.children[segment]
        if !exists {
            return nil
        }
        node = child
    }
    return node.route
}
```

This gives O(k) lookup time for static routes, where k is the path length. It's much faster than iterating through a list of routes for each request.

<MermaidDiagram diagram={`
graph TD
    A[Root] --> B[api]
    A --> C[static]
    B --> D[v1]
    B --> E[v2]
    D --> F[users]
    D --> G[products]
    E --> H[auth]
    C --> I[css]
    C --> J[js]
`} />

### Load Balancing

Their weighted round-robin load balancing is interesting:

```go
type WeightedRoundRobinLB struct {
    endpoints []*Endpoint
    weights   []int
    current   int
    mu        sync.Mutex
}

func (lb *WeightedRoundRobinLB) Next() *Endpoint {
    lb.mu.Lock()
    defer lb.mu.Unlock()

    totalWeight := 0
    for _, w := range lb.weights {
        totalWeight += w
    }

    for {
        lb.current = (lb.current + 1) % len(lb.endpoints)
        if lb.current == 0 {
            totalWeight--
            if totalWeight < 0 {
                totalWeight = 0
                for _, w := range lb.weights {
                    totalWeight += w
                }
            }
        }
        if totalWeight == 0 || lb.weights[lb.current] > totalWeight {
            return lb.endpoints[lb.current]
        }
    }
}
```

This allows for fine-grained control over traffic distribution. The time complexity is O(n) in the worst case, where n is the number of endpoints, but in practice, it's often much better.

## 4. Dynamic Configuration

One thing that really stands out is Skipper's ability to update routing configuration on the fly. This is super useful in Kubernetes environments. Here's a snippet from their Kubernetes ingress controller:

```go
func (c *Client) LoadAll() ([]*eskip.Route, error) {
    ingresses, err := c.getIngresses()
    if err != nil {
        return nil, err
    }

    routes := make([]*eskip.Route, 0, len(ingresses))
    for _, ing := range ingresses {
        rs, err := c.ingressToRoutes(ing)
        if err != nil {
            klog.Errorf("error converting ingress %v/%v to routes: %v", ing.Namespace, ing.Name, err)
            continue
        }
        routes = append(routes, rs...)
    }

    return routes, nil
}
```

This function is part of a loop that periodically checks for changes in Kubernetes Ingress resources and updates Skipper's routing table. The time complexity here is O(n * m), where n is the number of ingresses and m is the average number of routes per ingress.

## 5. Observability

I'm still exploring their observability features, but Skipper integrates well with Prometheus for metrics collection. Here's a basic example of how they set up a histogram for request durations:

```go
var (
    requestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "skipper_serve_route_duration_seconds",
            Help:    "The duration in seconds of serving requests.",
            Buckets: []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10},
        },
        []string{"route"},
    )
)

func init() {
    prometheus.MustRegister(requestDuration)
}
```

This allows for detailed monitoring of request latencies across different routes.

---

I'm still digging into their codebase, but I'm impressed by what I've seen so far. The attention to performance optimization, from low-level memory management to high-level routing strategies, is evident throughout.

While Nginx and HAProxy are solid, Skipper's Go-based architecture and focus on dynamic configuration make it a great fit for our container-based setup. Its extensibility has allowed us to implement custom logic that would have been tricky with other solutions.
 If you're dealing with high-traffic scenarios or complex routing needs, Skipper is definitely worth a look.

The code is [well-documented](https://opensource.zalando.com/skipper/reference/architecture/), and the community seems pretty active in slack.

Peace.

</WritingsArticleLayout>]]></content>
        <author>
            <name>Raghunandhan VR</name>
            <email>raghunandhanvr@outlook.com</email>
            <uri>https://raghu.app/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[db]]></title>
        <id>https://raghu.app/writings/db</id>
        <link href="https://raghu.app/writings/db"/>
        <updated>2026-04-10T12:40:23.148Z</updated>
        <content type="html"><![CDATA[import { WritingsArticleLayout } from "@/app/components/writings/writings-article-layout";
import { BlogViewCounter } from "@/app/components/ui/blog-view-counter";

export const article = {
  title: "Beyond the Big Three DBs",
  description:
    "A look at some other database options that are worth considering.",
};

export const metadata = {
  title: "Other DB Options",
  description: article.description,
  alternates: {
    canonical: "/writings/db",
  },
  openGraph: {
    images: [
      {
        url: `/api/og?title=Other+DB+Options`,
        width: 1200,
        height: 630,
      },
    ],
  },
};

<WritingsArticleLayout title={article.title} description={article.description} meta={<BlogViewCounter slug="/writings/db" createdAt={new Date('2024-08-10')} />}>

## PlanetScale (MySQL)

[PlanetScale](https://planetscale.com/) uses Vitess, developed at YouTube, for advanced sharding and scaling. Features like database branching (similar to Git branching) and Github's Ghost for schema migrations make it a solid option for MySQL. PlanetScale is my personal choice, and they also provide great knowledge transfer for Vitess on their [YouTube channel](https://www.youtube.com/c/PlanetScale).

## Neon.tech (Serverless PostgreSQL)

[Neon](https://neon.tech/) offers a serverless PostgreSQL that automatically scales up or down with zero downtime, saving costs and optimizing performance. It also has database branching. Keep in mind, 'serverless' is just a fancy term—they handle the servers for us, so we don't need to worry about scaling or maintenance, but our database is still running on servers somewhere.

## TimescaleDB (PostgreSQL extension)

[TimescaleDB](https://www.timescale.com/) is a time-series database built on PostgreSQL, perfect for handling time-series data. It might be helpful for blockchain teams and those dealing with logging. TimescaleDB is open-source.

## DragonflyDB (Redis alternative)

[DragonflyDB](https://dragonflydb.io/) is faster and more memory-efficient than Redis, fully compatible with Redis protocols for efficient caching. In a project where Redis was handling a large volume of caching for session management and API rate limiting, switching to DragonflyDB allowed us to handle more traffic with the same resources, reduce memory consumption, and keep costs down, all while being fully compatible with our existing Redis setup.

## ClickHouse (Analytical DB)

[ClickHouse](https://clickhouse.com/), developed by Yandex, is excellent for real-time analytical reporting with SQL, handling huge datasets efficiently. Its retrieval speed is really surprising. ClickHouse is open-source.

## CockroachDB (NewSQL)

[CockroachDB](https://www.cockroachlabs.com/) is cloud-native and SQL-compatible, ensuring robust transaction processing across regions without the high costs.

Yes, I know I've missed a few, like [Xata](https://xata.io/), [AWS Aurora](https://aws.amazon.com/rds/aurora/), etc. But, the focus here is on services that truly make "cost-effective" worth considering. These solutions are not only ideal for developers handling side projects but also beneficial for enterprise-level companies aiming to optimize costs and scale their infrastructure efficiently.

</WritingsArticleLayout>
]]></content>
        <author>
            <name>Raghunandhan VR</name>
            <email>raghunandhanvr@outlook.com</email>
            <uri>https://raghu.app/</uri>
        </author>
    </entry>
</feed>