<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" 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">
    <channel>
        <title>Blog by William Sun</title>
        <link>https://wsun.io</link>
        <description>Articles on Software Engineering, AI, etc.</description>
        <lastBuildDate>Wed, 11 Feb 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <ttl>60</ttl>
        <copyright>© 2026 William Sun</copyright>
        <atom:link href="https://wsun.io/feed.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Go Reflection Deep Dive: Type, Value, and Practical Patterns]]></title>
            <link>https://wsun.io/blog/go-reflection</link>
            <guid isPermaLink="false">https://wsun.io/blog/go-reflection</guid>
            <pubDate>Wed, 11 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A comprehensive guide to Go's reflect package, covering Type introspection, Value manipulation, and creating objects dynamically.]]></description>
            <content:encoded><![CDATA[<hr>
<h2>title: "Go Reflection Deep Dive: Type, Value, and Practical Patterns"
summary: "A comprehensive guide to Go's reflect package, covering Type introspection, Value manipulation, and creating objects dynamically."
date: 2026-02-11
tags: ["Go", "Reflection", "reflect", "Type", "Value", "Golang"]
featured: false</h2>
<h1>Go Reflection Deep Dive: Type, Value, and Practical Patterns</h1>
<p>Reflection gives Go programs the ability to inspect and manipulate types and values at runtime. It is one of the most powerful features in the language, but that power comes at a cost. Before reaching for the reflect package, keep two things in mind:</p>
<ol>
<li>Code that uses reflection is hard to read, hard to maintain, and prone to runtime panics in production</li>
<li>Performance is poor, typically one to two orders of magnitude slower than equivalent non reflective code</li>
</ol>
<p>With that said, reflection is indispensable in certain scenarios such as serialization, ORMs, and generic frameworks. The two most important concepts in Go's reflection are Type and Value. Type is used to obtain type related information (such as the length of a Slice, the fields of a struct, or the number of parameters of a function). Value is used to get and modify the values of the original data (such as modifying elements in a slice or map, or modifying struct fields). The conversion between these and Go's primitive data types (such as int, float, map, struct, etc.) is illustrated in the diagram below:</p>
<div></div>
<p>All the methods shown in the diagram above will be covered below. We'll start with Type, then cover Value.</p>
<p>Throughout this post, the examples use the following types:</p>
<pre><code class="language-go">type People interface {
	Think()
}

type User struct {
	Id     int     `json:"id"`
	Name   string  `json:"name"`
	addr   string  // unexported field
	Weight float64 `json:"weight"`
	Height float64 `json:"height"`
}

func (u *User) BMI() float32 {
	return float32(u.Weight / (u.Height * u.Height))
}

func (u User) Think() {
	fmt.Printf("%s is thinking\n", u.Name)
}
</code></pre>
<h2>reflect.Type</h2>
<h3>How to Get a Type</h3>
<p>Use TypeOf() to get a Type:</p>
<pre><code class="language-go">typeI := reflect.TypeOf(1)
typeS := reflect.TypeOf("hello")
fmt.Println(typeI)  // int
fmt.Println(typeS)  // string

typeUser := reflect.TypeOf(&#x26;User{})
fmt.Println(typeUser)                // *User
fmt.Println(typeUser.Kind())         // ptr
fmt.Println(typeUser.Elem().Kind())  // struct
</code></pre>
<h3>Converting Pointer Type to Non Pointer Type</h3>
<pre><code class="language-go">typeUser := reflect.TypeOf(&#x26;User{})
typeUser2 := reflect.TypeOf(User{})
assert.IsEqual(typeUser.Elem(), typeUser2)
</code></pre>
<h3>Getting Struct Field Information</h3>
<pre><code class="language-go">typeUser := reflect.TypeOf(User{})  // Must use struct's Type, not the pointer's Type
fieldNum := typeUser.NumField()     // Number of fields
for i := 0; i &#x3C; fieldNum; i++ {
	field := typeUser.Field(i)
	fmt.Printf("%d %s offset %d anonymous %t type %s exported %t json tag %s\n", i,
		field.Name,            // Field name
		field.Offset,          // Memory offset relative to the struct's base address; string type occupies 16 bytes
		field.Anonymous,       // Whether it is an anonymous (embedded) field
		field.Type,            // Data type, of reflect.Type
		field.IsExported(),    // Whether it is visible outside the package (i.e., starts with an uppercase letter)
		field.Tag.Get("json")) // Get the tag defined inside `` after the field
}
fmt.Println()

// You can get a Field by name using FieldByName
if nameField, ok := typeUser.FieldByName("Name"); ok {
	fmt.Printf("Name is exported %t\n", nameField.IsExported())
}
// You can also get a Field by index using FieldByIndex
thirdField := typeUser.FieldByIndex([]int{2})  // The parameter is a slice, because structs can be nested
fmt.Printf("third field name %s\n", thirdField.Name)
</code></pre>
<h3>Getting Struct Method Information</h3>
<pre><code class="language-go">typeUser := reflect.TypeOf(User{})
methodNum := typeUser.NumMethod()  // Number of methods. Methods with pointer receivers are NOT included
for i := 0; i &#x3C; methodNum; i++ {
	method := typeUser.Method(i)
	fmt.Printf("method name:%s ,type:%s, exported:%t\n", method.Name, method.Type, method.IsExported())
}
fmt.Println()

typeUser2 := reflect.TypeOf(&#x26;User{})
methodNum = typeUser2.NumMethod()  // Number of methods. Methods with BOTH pointer and value receivers are included, meaning methods implemented by the value are also implemented by the pointer (but not vice versa)
for i := 0; i &#x3C; methodNum; i++ {
	method := typeUser2.Method(i)
	fmt.Printf("method name:%s ,type:%s, exported:%t\n", method.Name, method.Type, method.IsExported())
}
</code></pre>
<h3>Getting Function Information</h3>
<pre><code class="language-go">func Add(a, b int) int {
	return a + b
}

typeFunc := reflect.TypeOf(Add)  // Get the function's type
fmt.Printf("is function type %t\n", typeFunc.Kind() == reflect.Func)
argInNum := typeFunc.NumIn()    // Number of input parameters
argOutNum := typeFunc.NumOut()  // Number of output parameters
for i := 0; i &#x3C; argInNum; i++ {
	argTyp := typeFunc.In(i)
	fmt.Printf("Input parameter %d has type %s\n", i, argTyp)
}
for i := 0; i &#x3C; argOutNum; i++ {
	argTyp := typeFunc.Out(i)
	fmt.Printf("Output parameter %d has type %s\n", i, argTyp)
}
</code></pre>
<h3>Checking if a Type Implements an Interface</h3>
<pre><code class="language-go">// Get the interface type via reflect.TypeOf((*&#x3C;interface>)(nil)).Elem(). Since People is an interface and cannot be instantiated, we cast nil to *People
typeOfPeople := reflect.TypeOf((*People)(nil)).Elem()
fmt.Printf("typeOfPeople kind is interface %t\n", typeOfPeople.Kind() == reflect.Interface)
t1 := reflect.TypeOf(User{})
t2 := reflect.TypeOf(&#x26;User{})
// If the value type implements the interface, then the pointer type also implements it; the reverse is not true
fmt.Printf("t1 implements People interface %t\n", t1.Implements(typeOfPeople))
</code></pre>
<h2>reflect.Value</h2>
<h3>How to Get a Value</h3>
<p>Use ValueOf() to get a Value:</p>
<pre><code class="language-go">iValue := reflect.ValueOf(1)
sValue := reflect.ValueOf("hello")
userPtrValue := reflect.ValueOf(&#x26;User{
	Id:     7,
	Name:   "John",
	Weight: 65,
	Height: 1.68,
})
fmt.Println(iValue)        // 1
fmt.Println(sValue)        // hello
fmt.Println(userPtrValue)  // &#x26;{7 John  65 1.68}
</code></pre>
<h3>Converting Value to Type</h3>
<pre><code class="language-go">iType := iValue.Type()
sType := sValue.Type()
userType := userPtrValue.Type()
// Calling Kind() on both the Type and the corresponding Value yields the same result
fmt.Println(iType.Kind() == reflect.Int, iValue.Kind() == reflect.Int, iType.Kind() == iValue.Kind())
fmt.Println(sType.Kind() == reflect.String, sValue.Kind() == reflect.String, sType.Kind() == sValue.Kind())
fmt.Println(userType.Kind() == reflect.Ptr, userPtrValue.Kind() == reflect.Ptr, userType.Kind() == userPtrValue.Kind())
</code></pre>
<h3>Converting Between Pointer Value and Non Pointer Value</h3>
<pre><code class="language-go">userValue := userPtrValue.Elem()                     // Elem() converts a pointer Value to a non pointer Value
fmt.Println(userValue.Kind(), userPtrValue.Kind())   // struct ptr
userPtrValue3 := userValue.Addr()                    // Addr() converts a non pointer Value to a pointer Value
fmt.Println(userValue.Kind(), userPtrValue3.Kind())  // struct ptr
</code></pre>
<h3>Getting the Original Data from a Value</h3>
<p>Use the Interface() function to convert a Value to interface, then use a type assertion to convert from interface to the original data type. Alternatively, call Int(), String(), etc. directly on the Value for a one step conversion.</p>
<pre><code class="language-go">fmt.Printf("origin value iValue is %d %d\n", iValue.Interface().(int), iValue.Int())
fmt.Printf("origin value sValue is %s %s\n", sValue.Interface().(string), sValue.String())
user := userValue.Interface().(User)
fmt.Printf("id=%d name=%s weight=%.2f height=%.2f\n", user.Id, user.Name, user.Weight, user.Height)
user2 := userPtrValue.Interface().(*User)
fmt.Printf("id=%d name=%s weight=%.2f height=%.2f\n", user2.Id, user2.Name, user2.Weight, user2.Height)
</code></pre>
<h3>Checking for Empty/Invalid Values</h3>
<pre><code class="language-go">var i interface{}  // Interface not pointing to a concrete value
v := reflect.ValueOf(i)
fmt.Printf("v holds a value: %t, type of v is Invalid: %t\n", v.IsValid(), v.Kind() == reflect.Invalid)

var user *User = nil
v = reflect.ValueOf(user)  // Value points to nil
if v.IsValid() {
	fmt.Printf("the value held by v is nil: %t\n", v.IsNil())  // Call IsNil() only after confirming IsValid(), otherwise it will panic
}

var u User  // Only declared, all fields are zero values
v = reflect.ValueOf(u)
if v.IsValid() {
	fmt.Printf("the value held by v is the zero value of its type: %t\n", v.IsZero())  // Call IsZero() only after confirming IsValid(), otherwise it will panic
}
</code></pre>
<h3>Modifying Original Data via Value</h3>
<pre><code class="language-go">var i int = 10
var s string = "hello"
user := User{
	Id:     7,
	Name:   "John",
	Weight: 65.5,
	Height: 1.68,
}

valueI := reflect.ValueOf(&#x26;i)  // Since all function arguments in Go are passed by value, you must pass a pointer to modify the original value
valueS := reflect.ValueOf(&#x26;s)
valueUser := reflect.ValueOf(&#x26;user)
valueI.Elem().SetInt(8)  // Since valueI corresponds to a pointer, use Elem() to get the object the pointer points to
valueS.Elem().SetString("golang")
valueUser.Elem().FieldByName("Weight").SetFloat(68.0)  // FieldByName() returns a struct field by name
</code></pre>
<p>Important: to modify the original data, you must pass a pointer to ValueOf. However, you cannot call Set or FieldByName on a pointer Value, so you must first convert it to a non pointer Value using Elem().</p>
<p>Unexported fields cannot be modified via reflection.</p>
<pre><code class="language-go">addrValue := valueUser.Elem().FieldByName("addr")
if addrValue.CanSet() {
	addrValue.SetString("New York")
} else {
	fmt.Println("addr is an unexported field and cannot be Set")  // Fields starting with a lowercase letter are effectively private
}
</code></pre>
<h3>Modifying a Slice via Value</h3>
<pre><code class="language-go">users := make([]*User, 1, 5)  // len=1, cap=5
users[0] = &#x26;User{
	Id:     7,
	Name:   "John",
	Weight: 65.5,
	Height: 1.68,
}

sliceValue := reflect.ValueOf(&#x26;users)  // Pass the address of users since we intend to modify it via Value
if sliceValue.Elem().Len() > 0 {       // Get the length of the slice
	sliceValue.Elem().Index(0).Elem().FieldByName("Name").SetString("Mike")
	fmt.Printf("1st user name change to %s\n", users[0].Name)
}
</code></pre>
<p>You can even modify the slice's cap. The new cap must be between the original len and cap, meaning you can only reduce the cap.</p>
<pre><code class="language-text">sliceValue.Elem().SetCap(3)
</code></pre>
<p>By increasing the len, you can effectively append elements to the slice.</p>
<pre><code class="language-go">sliceValue.Elem().SetLen(2)
// Use reflect.Value's Set() function to modify the underlying original data
sliceValue.Elem().Index(1).Set(reflect.ValueOf(&#x26;User{
	Id:     8,
	Name:   "Sarah",
	Weight: 80,
	Height: 180,
}))
fmt.Printf("2nd user name %s\n", users[1].Name)
</code></pre>
<h3>Modifying a Map</h3>
<p>Value.SetMapIndex(): adds a key value pair to the map</p>
<p>Value.MapIndex(): retrieves the value corresponding to a key from the map</p>
<pre><code class="language-go">u1 := &#x26;User{
	Id:     7,
	Name:   "John",
	Weight: 65.5,
	Height: 1.68,
}
u2 := &#x26;User{
	Id:     8,
	Name:   "Tom",
	Weight: 65.5,
	Height: 1.68,
}
userMap := make(map[int]*User, 5)
userMap[u1.Id] = u1

mapValue := reflect.ValueOf(&#x26;userMap)                                                         // Pass the address of userMap since we intend to modify it via Value
mapValue.Elem().SetMapIndex(reflect.ValueOf(u2.Id), reflect.ValueOf(u2))                      // SetMapIndex adds a key value pair to the map
mapValue.Elem().MapIndex(reflect.ValueOf(u1.Id)).Elem().FieldByName("Name").SetString("Mike")  // MapIndex retrieves the value for a key from the map
for k, user := range userMap {
	fmt.Printf("key %d name %s\n", k, user.Name)
}
</code></pre>
<h3>Calling Functions</h3>
<pre><code class="language-go">valueFunc := reflect.ValueOf(Add)  // A function is also a data type
typeFunc := reflect.TypeOf(Add)
argNum := typeFunc.NumIn()             // Number of input parameters
args := make([]reflect.Value, argNum)  // Prepare the function's input arguments
for i := 0; i &#x3C; argNum; i++ {
	if typeFunc.In(i).Kind() == reflect.Int {
		args[i] = reflect.ValueOf(3)  // Assign 3 to each parameter
	}
}
sumValue := valueFunc.Call(args)  // Returns []reflect.Value, because Go functions can return multiple values
if typeFunc.Out(0).Kind() == reflect.Int {
	sum := sumValue[0].Interface().(int)  // Convert from Value to the original data type
	fmt.Printf("sum=%d\n", sum)
}
</code></pre>
<h3>Calling Methods</h3>
<pre><code class="language-go">user := User{
	Id:     7,
	Name:   "John",
	Weight: 65.5,
	Height: 1.68,
}
valueUser := reflect.ValueOf(&#x26;user)               // Must pass a pointer, because BMI() is defined with a pointer receiver
bmiMethod := valueUser.MethodByName("BMI")         // MethodByName() returns a method by name
resultValue := bmiMethod.Call([]reflect.Value{})    // Pass an empty slice when there are no parameters
result := resultValue[0].Interface().(float32)
fmt.Printf("bmi=%.2f\n", result)

// Think() is defined with a value receiver, so valueUser can be either a pointer or a value
thinkMethod := valueUser.MethodByName("Think")
thinkMethod.Call([]reflect.Value{})

valueUser2 := reflect.ValueOf(user)
thinkMethod = valueUser2.MethodByName("Think")
thinkMethod.Call([]reflect.Value{})
</code></pre>
<h2>Creating Objects</h2>
<h3>Creating a Struct</h3>
<pre><code class="language-go">t := reflect.TypeOf(User{})
value := reflect.New(t)  // Create an object based on reflect.Type, get a pointer to it, then get the reflect.Value from the pointer
value.Elem().FieldByName("Id").SetInt(10)
user := value.Interface().(*User)  // Convert the reflection type back to a Go primitive data type
</code></pre>
<h3>Creating a Slice</h3>
<pre><code class="language-go">var slice []User
sliceType := reflect.TypeOf(slice)
sliceValue := reflect.MakeSlice(sliceType, 1, 3)
sliceValue.Index(0).Set(reflect.ValueOf(User{
	Id:     8,
	Name:   "Sarah",
	Weight: 80,
	Height: 180,
}))
users := sliceValue.Interface().([]User)
fmt.Printf("1st user name %s\n", users[0].Name)
</code></pre>
<h3>Creating a Map</h3>
<pre><code class="language-go">var userMap map[int]*User
mapType := reflect.TypeOf(userMap)
// mapValue := reflect.MakeMap(mapType)
mapValue := reflect.MakeMapWithSize(mapType, 10)

user := &#x26;User{
	Id:     7,
	Name:   "John",
	Weight: 65.5,
	Height: 1.68,
}
key := reflect.ValueOf(user.Id)
mapValue.SetMapIndex(key, reflect.ValueOf(user))                      // SetMapIndex adds a key value pair to the map
mapValue.MapIndex(key).Elem().FieldByName("Name").SetString("Mike")   // MapIndex retrieves the value for a key from the map
userMap = mapValue.Interface().(map[int]*User)
fmt.Printf("user name %s %s\n", userMap[7].Name, user.Name)
</code></pre>
<p>In addition to MakeSlice() and MakeMap(), the reflect package also provides MakeChan() and MakeFunc().</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[[LeetCode] Binary Search Without the Edge Case Headaches]]></title>
            <link>https://wsun.io/blog/leetcode-binary-search-template</link>
            <guid isPermaLink="false">https://wsun.io/blog/leetcode-binary-search-template</guid>
            <pubDate>Sun, 10 Aug 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A single template for all binary search problems.]]></description>
            <content:encoded><![CDATA[<hr>
<h2>title: "[LeetCode] Binary Search Without the Edge Case Headaches"
summary: "A single template for all binary search problems."
date: 2025-08-10
tags: ["Binary Search", "Algorithms", "LeetCode", "Python"]
featured: false</h2>
<h1>Binary Search Without the Edge Case Headaches</h1>
<p>Binary search is supposed to be simple. Split the array in half, check the middle, repeat. But anyone who's spent time on LeetCode knows the real difficulty isn't the idea -- it's the implementation. <code>left &#x3C; right</code> or <code>left &#x3C;= right</code>? <code>mid</code> or <code>mid + 1</code>? Return <code>left</code> or <code>right</code>? Every problem feels like a fresh puzzle of off-by-one errors.</p>
<p>I used to re-derive the boundary logic for every single binary search problem. Eventually I settled on a fixed template that I've used on dozens of problems without failure. Here's the template, and more importantly, why it works.</p>
<h2>The Template</h2>
<pre><code class="language-python">left, right = 0, n - 1
while left &#x3C;= right:
    mid = (right - left) // 2 + left
    if condition:
        left = mid + 1
    else:
        right = mid - 1
</code></pre>
<p>Three rules, no exceptions:</p>
<ol>
<li>Loop condition is always <code>while left &#x3C;= right</code></li>
<li>Updates are always <code>left = mid + 1</code> and <code>right = mid - 1</code></li>
<li>Never return inside the loop -- decide what to return after the loop exits</li>
</ol>
<h2>Why It Works</h2>
<p>When the loop exits, <code>left = right + 1</code>. Always. This isn't a heuristic -- you can prove it.</p>
<p>The loop runs as long as <code>left &#x3C;= right</code>, so the interval <code>[left, right]</code> is non-empty on every iteration. Each branch strictly shrinks the interval because <code>mid</code> always lies within <code>[left, right]</code>. When the interval finally shrinks to <code>left == right</code>, we get <code>mid == left == right</code>, and one more branch pushes them past each other. The only possible exit state is:</p>
<div><div><p><div>...</div>
<div>arr[right]</div>
<div>arr[left]</div>
<div>...</div></p></div><div><div></div><div><div>'|'</div><p><div>right</div></p></div><div><div>'|'</div><p><div>left</div></p></div></div><div><div>'&#x3C;-- left half -->'</div><div>'&#x3C;-- right half -->'</div></div></div>
<p>The array has been partitioned into two halves. Every element that triggered <code>left = mid + 1</code> is now on the left side (at or before <code>right</code>). Every element that triggered <code>right = mid - 1</code> is on the right side (at or after <code>left</code>).</p>
<p>So <code>right</code> is the last element of the left half, and <code>left</code> is the first element of the right half.</p>
<h2>Deciding the Return Value</h2>
<p>Ask one question: which branch did my target get swept into?</p>
<p>If the target satisfies the condition that triggers <code>left = mid + 1</code>, it ended up in the left half. The last such element is <code>right</code>. <strong>Return <code>right</code>.</strong></p>
<p>If the target satisfies the condition that triggers <code>right = mid - 1</code>, it ended up in the right half. The first such element is <code>left</code>. <strong>Return <code>left</code>.</strong></p>
<p>That's the entire decision process. No case analysis, no finger-tracing through examples.</p>
<h2>Example: <a href="https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/">LeetCode 34</a> (Find First and Last Position)</h2>
<p>This problem asks for the leftmost and rightmost positions of a target in a sorted array. It needs two binary searches.</p>
<p><strong>Finding the left boundary</strong> (first occurrence of target):</p>
<pre><code class="language-python">def find_left(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo &#x3C;= hi:
        mid = (lo + hi) // 2
        if nums[mid] &#x3C; target:
            lo = mid + 1
        else:
            hi = mid - 1
    return lo
</code></pre>
<p>When <code>nums[mid] == target</code>, we go into the <code>else</code> branch (<code>hi = mid - 1</code>), pushing <code>hi</code> left past the match. So equal-to-target elements get swept by <code>hi = mid - 1</code>, landing in the right half. Return <code>lo</code>.</p>
<p><strong>Finding the right boundary</strong> (last occurrence of target):</p>
<pre><code class="language-python">def find_right(nums, target):
    lo, hi = 0, len(nums) - 1
    while lo &#x3C;= hi:
        mid = (lo + hi) // 2
        if nums[mid] &#x3C;= target:
            lo = mid + 1
        else:
            hi = mid - 1
    return hi
</code></pre>
<p>When <code>nums[mid] == target</code>, we hit <code>lo = mid + 1</code>, pushing <code>lo</code> right past the match. Equal-to-target elements get swept by <code>lo = mid + 1</code>, landing in the left half. Return <code>hi</code>.</p>
<p><strong>Putting it together:</strong></p>
<pre><code class="language-python">class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def find_left():
            lo, hi = 0, len(nums) - 1
            while lo &#x3C;= hi:
                mid = (lo + hi) // 2
                if nums[mid] &#x3C; target:
                    lo = mid + 1
                else:
                    hi = mid - 1
            return lo

        def find_right():
            lo, hi = 0, len(nums) - 1
            while lo &#x3C;= hi:
                mid = (lo + hi) // 2
                if nums[mid] &#x3C;= target:
                    lo = mid + 1
                else:
                    hi = mid - 1
            return hi

        l, r = find_left(), find_right()
        return [l, r] if l &#x3C;= r else [-1, -1]
</code></pre>
<p>The only difference between the two functions is <code>&#x3C;</code> vs <code>&#x3C;=</code>. That single character controls whether equal elements get pushed left or right.</p>
<h2>Boundary Checks</h2>
<p>The pointers can land outside the array. If every element is greater than the target, <code>right</code> ends up at -1. If every element is smaller, <code>left</code> ends up at <code>n</code>. Both cases still satisfy <code>left = right + 1</code> -- the invariant holds even at the extremes.</p>
<p>So after the loop, always check that your returned index is within <code>[0, n-1]</code> and that <code>nums[index] == target</code> (or whatever your problem requires). The template guarantees the partition is correct; you just need to verify the answer exists.</p>
<h2>Other Examples</h2>
<p><strong><a href="https://leetcode.com/problems/first-bad-version/">LeetCode 278</a> (First Bad Version):</strong> Classic <code>FFFFTTTTT</code> partition. <code>isBadVersion(mid)</code> is true -> <code>hi = mid - 1</code>. Answer swept by <code>hi</code>, return <code>lo</code>.</p>
<p><strong><a href="https://leetcode.com/problems/search-insert-position/">LeetCode 35</a> (Search Insert Position):</strong> Find the first index where <code>nums[i] >= target</code>. <code>nums[mid] >= target</code> -> <code>hi = mid - 1</code>. Answer swept by <code>hi</code>, return <code>lo</code>.</p>
<p><strong>Finding the last element '&#x3C;=' target:</strong> <code>nums[mid] &#x3C;= target</code> -> <code>lo = mid + 1</code>. Answer swept by <code>lo</code>, return <code>hi</code>.</p>
<p>Same template, same reasoning, every time.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How gRPC Powers Recommendation and Inference in Django]]></title>
            <link>https://wsun.io/blog/grpc-integration-gastronome</link>
            <guid isPermaLink="false">https://wsun.io/blog/grpc-integration-gastronome</guid>
            <pubDate>Thu, 03 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Explore how Gastronome uses gRPC to handle ML-based recommendations and sentiment scoring asynchronously.]]></description>
            <content:encoded><![CDATA[<hr>
<h2>title: "How gRPC Powers Recommendation and Inference in Django"
summary: "Explore how Gastronome uses gRPC to handle ML-based recommendations and sentiment scoring asynchronously."
date: 2025-07-03
tags: ["gRPC", "Django", "Microservices", "Recommendation System", "Celery", "Redis"]
featured: true</h2>
<h1>How gRPC Powers Recommendation and Inference in Django</h1>
<p>Gastronome is a sophisticated Django-based recommendation platform designed to provide users with personalized business suggestions, powered by advanced machine learning models. To achieve a highly responsive and scalable architecture, Gastronome leverages <strong>gRPC</strong> - a high-performance Remote Procedure Call (RPC) framework - to handle computationally intensive tasks asynchronously. This approach ensures the Django web application remains lightweight, fast, and responsive by offloading heavy workloads like recommendation computations and semantic sentiment scoring to dedicated microservices.</p>
<h2>What is gRPC and Why Use It?</h2>
<p><a href="https://grpc.io/">gRPC</a> is an open-source framework developed by Google that enables efficient, language-neutral RPC communication between distributed systems. It relies on HTTP/2 for transport, uses Protocol Buffers (protobufs) for serialization, and supports bi-directional streaming. These attributes make gRPC especially suited for performance-critical applications and internal microservice communications.</p>
<h2>gRPC in Gastronome's Architecture</h2>
<p>Gastronome employs a modular microservices architecture, where the main Django web application interacts with two dedicated gRPC-powered services:</p>
<ul>
<li><strong>Recommendation Service</strong>: Handles user-specific recommendation computations using collaborative filtering and matrix factorization methods, generating personalized business recommendations.</li>
<li><strong>Inference Service</strong>: Performs sentiment analysis using a fine-tuned DistilBERT model, assigning semantic sentiment scores to user reviews to enrich recommendations and user profiling.</li>
</ul>
<p>The Django web app itself doesn't perform heavy computations inline. Instead, Celery asynchronous workers handle these tasks, interacting with the two gRPC microservices to process tasks in the background, ensuring minimal latency for user requests.</p>
<h2>Illustrative Use Cases and Workflows</h2>
<p>To clearly understand how Gastronome employs gRPC, let's explore two workflows within the platform:</p>
<h3>1. Cache-miss Personalized Recommendation Flow</h3>
<div></div>
<p>In this scenario, when a user requests personalized recommendations and there's a cache miss in Redis, Gastronome immediately returns a fallback popular recommendation list to ensure rapid response. Simultaneously, a Celery worker is dispatched asynchronously to call the Recommendation Service via gRPC. This service retrieves or trains the appropriate recommendation model and computes personalized recommendations. Upon completion, the results are cached in Redis, significantly accelerating future retrievals.</p>
<h3>2. New Review Submission &#x26; Sentiment Analysis Flow</h3>
<div></div>
<p>Here, Gastronome captures new user reviews quickly, storing them immediately in PostgreSQL with a placeholder sentiment score. A Celery worker then asynchronously contacts the gRPC-based Inference Service, where a fine-tuned DistilBERT model processes the review text to determine semantic sentiment. Once analyzed, the sentiment score is returned via gRPC, and the worker updates the review in the database, enhancing user profiles and future recommendations.</p>
<h2>Why Celery and gRPC Work Together So Well</h2>
<ul>
<li><strong>Robust Scalability</strong>: By offloading heavy workloads to Celery workers and communicating via gRPC, Gastronome can independently scale recommendation and inference services without affecting the main Django application's responsiveness.</li>
<li><strong>Language Agnostic</strong>: gRPC enables microservices to be written in different programming languages. This flexibility allows you to pick the best language or framework for each service, making it easier to evolve or optimize parts of the system over time.</li>
<li><strong>Robustness</strong>: gRPC natively supports advanced reliability features like retries, deadlines (timeouts), and circuit breakers. Combined with Celery's task management and retry mechanisms, this architecture provides strong fault tolerance and keeps the system resilient even if individual services fail or become temporarily unavailable.</li>
</ul>
<h2>Closing Thoughts</h2>
<p>Leveraging gRPC within Django is a powerful architectural decision that significantly enhances application maintainability and scalability. Gastronome's implementation showcases an effective pattern for Django applications that require advanced ML computations without sacrificing user experience.</p>
<p>By isolating ML workloads into dedicated microservices and communicating asynchronously through gRPC, Gastronome efficiently blends advanced machine learning capabilities with robust web application performance.</p>]]></content:encoded>
        </item>
    </channel>
</rss>