Blog
Practical writing on AI engineering, infrastructure, backend systems, and production lessons learned.
Category
154 posts found
Archive
Browse the archive page by page for a faster, cleaner reading experience.
147 posts total
Personalizing Elasticsearch Results: function_score, Rescoring, and Query-Time vs Index-Time Tradeoffs
•3 min read•Databases & StorageElasticsearch's BM25 scoring knows nothing about your users. Personalization requires either modifying the query at request time (function_score, pinned queries) or pre-computing signals at index time. Each approach has different latency, freshness, and complexity costs.
searchelasticsearchpersonalizationrelevanceElasticsearch Full-Text Search: Inverted Index, BM25, and the Analyzer Pipeline
•2 min read•Databases & StorageFull-text search in Elasticsearch works through three layers: the analyzer pipeline (tokenization, normalization, stemming), the inverted index (term → document mapping), and BM25 scoring (term frequency saturation + document length normalization). Understanding these layers explains why match queries behave differently than term queries and why field length matters for relevance.
searchelasticsearchElasticsearch Query Types: When to Use match, term, range, and bool
•2 min read•Databases & StorageElasticsearch query types split into analyzed (full-text) and non-analyzed (exact-match) categories. match analyzes the input through the field's analyzer before lookup; term does not. Choosing the wrong query type for a field's mapping causes silent 0-result bugs. Query context contributes to _score; filter context doesn't — use filter for binary conditions to enable caching.
searchelasticsearchMapReduce: The Programming Model That Made Big Data Tractable
•4 min read•Distributed SystemsMapReduce's insight was not the map and reduce functions — those come from functional programming. The insight was that a runtime could automatically parallelize, fault-tolerate, and shuffle those functions across thousands of machines. Understanding the shuffle phase and fault tolerance explains both MapReduce's power and its limits.
algorithmsmapreducebatch-processingpapersGFS: Design Decisions That Shaped Distributed Storage
•5 min read•Distributed SystemsThe Google File System paper (2003) made design choices that looked unconventional — a single master, relaxed consistency, large chunk sizes. Understanding why those choices were made reveals the constraints of large-scale distributed storage.
storagegfsarchitecturepapersBloom Filter: Probabilistic Membership with Tunable False Positive Rate
•2 min read•ProgrammingA Bloom filter is a space-efficient probabilistic data structure that answers 'definitely not in set' or 'possibly in set'. It uses k hash functions to set bits in an m-bit array. False positives are possible; false negatives are not. The false positive rate is tunable via m and k. HBase, Cassandra, and Chrome's malicious URL checker all use Bloom filters.
algorithmsfilteringGo Loop Variables and Goroutines: The Closure Capture Bug and the Go 1.22 Fix
•3 min read•ProgrammingGoroutines in for loops have a classic capture bug: all goroutines share one loop variable and race to read its final value. Go 1.22 changed the semantics. Here is what that means for existing and new code.
programminggogoroutinesconcurrencyclosuresGo Fundamentals: nil Slices, Interface Type Assertions, and Goroutine Scheduling
•2 min read•ProgrammingThree Go behaviors that surprise developers from other languages: nil slices are safe to use (unlike null pointers), type assertions and type conversions on interfaces have different runtime semantics, and goroutines are multiplexed onto OS threads by the Go scheduler — not mapped 1:1 like pthreads.
gogoroutinesconcurrencyGo sync.Mutex: CAS Fast Path, Spinning, and Starvation Mode
•2 min read•ProgrammingGo's sync.Mutex avoids kernel syscalls on the uncontended fast path using a single atomic CAS instruction. On contention, goroutines spin (up to 4 times) before sleeping via a semaphore. If a goroutine waits more than 1ms, the mutex enters starvation mode: ownership transfers directly to the waiting goroutine, bypassing newly arriving goroutines.
gomutexconcurrencysynchronizationGo Race Detector: Finding Data Races with -race
•2 min read•ProgrammingGo's race detector instruments memory accesses at compile time using ThreadSanitizer. Run with -race to detect concurrent reads and writes without synchronization. The detector reports the exact goroutines, memory addresses, and source lines involved. It incurs 5-20× overhead — use in tests and staging, not production.
goconcurrencytestingReflection in Go: reflect.Type, reflect.Value, and When Not to Use It
•1 min read•ProgrammingGo's reflect package inspects and manipulates values at runtime when static types aren't known. reflect.TypeOf() returns the type descriptor. reflect.ValueOf() returns a Value that wraps the data. reflect.Value.CanSet() requires the value to be addressable (passed as pointer). Reflection is 10–100× slower than direct calls — use it for serialization frameworks and dependency injection, not hot paths.
goreflectionLate Static Binding in PHP: static:: vs self::
•1 min read•ProgrammingLate static binding (LSB) in PHP resolves the called class at runtime rather than the class where the method is defined. static:: refers to the class that was called; self:: always refers to the class where the code is written. Without LSB, factory methods and singleton patterns in parent classes return instances of the wrong class when called on subclasses.
phpoopZero-Downtime Database Migrations: The Expand/Contract Pattern
•3 min read•ProgrammingDeploying code and schema changes together is fragile — a failed deploy leaves code and schema out of sync. The expand/contract pattern decouples them: schema changes backward-compatible with both old and new code, applied in two separate deploys.
programming-thoughtsdatabasesdeploymentmigrationsMicroservices: The Costs Nobody Mentions Until You're Already Committed
•4 min read•ProgrammingMicroservices solve real scaling problems. They also add distributed systems complexity that compounds at every service boundary. Understanding the real costs helps you decide when the tradeoff is worth it.
programming-thoughtsarchitecturemicroservicesSliding Window Protocol: Reliable Delivery, Ordering, and Flow Control
•3 min read•Systems & NetworkingSliding window allows a sender to have multiple unacknowledged frames in flight simultaneously, improving throughput on high-latency links. The sender tracks LAR (last ACK received) and LFS (last frame sent); the receiver tracks NFE (next frame expected). Cumulative ACKs advance the send window; out-of-order frames buffer until gaps fill.
tcpflow-controlWebRTC: Peer-to-Peer Media Without a Media Server
•4 min read•Systems & NetworkingWebRTC enables direct browser-to-browser audio, video, and data transfer. The signaling, ICE negotiation, and NAT traversal are where complexity lives — and where most implementations break in production.
webrtcp2pudpstunturn0.0.0.0 vs 127.0.0.1: Bind Address Semantics
•1 min read•Systems & Networking0.0.0.0 means 'bind to all interfaces' — the process accepts connections on any network interface the host has. 127.0.0.1 is the loopback address — only accepts connections from the same machine. Binding to a specific IP restricts a service to one interface. The choice determines network reachability and is a security boundary.
networkinglinuxsocketIP Datagrams: Connectionless Packet Forwarding
•2 min read•Systems & NetworkingAn IP datagram carries its destination address in every packet header. Each router independently forwards the packet based only on the destination address and its local forwarding table — no prior connection setup, no per-flow state at routers. This connectionless design enables scalability but provides no delivery guarantees.
networkingiproutingHostname, Domain Name, FQDN, and DNS Resolution
•1 min read•Systems & NetworkingA hostname identifies a specific device. A domain name identifies an organization or service. An FQDN (Fully Qualified Domain Name) is the complete dot-separated name from host to root. DNS resolves FQDNs to IP addresses through a hierarchy of authoritative nameservers. The trailing dot in an FQDN marks the DNS root.
dnsnetworkingIP: Datagram Forwarding, Subnetting, ARP, and DHCP
•2 min read•Systems & NetworkingIP provides connectionless, best-effort packet delivery. Routers forward packets based on destination IP and longest-prefix match in their forwarding tables. Subnetting divides network addresses using a mask. ARP resolves IP to MAC address on the local link. DHCP automates IP assignment via a 4-step DORA exchange.
dhcparpudpSockets: The API Between Application Code and the Network Stack
•3 min read•Systems & NetworkingEvery network connection — TCP, UDP, Unix domain — goes through the socket API. Understanding the difference between blocking and non-blocking sockets, the role of the kernel's accept queue, and how event-driven servers avoid the thread-per-connection trap explains why high-concurrency servers are designed the way they are.
socketslinuxconcurrencyTCP: How Reliable Delivery Actually Works
•4 min read•Systems & NetworkingTCP makes strong delivery guarantees over an unreliable network. Understanding the mechanisms behind those guarantees — handshake, flow control, congestion control, TIME_WAIT — helps diagnose real production problems.
tcpprotocolsperformanceUDP: Connectionless Transport, Message Boundaries, and When to Use It
•2 min read•Systems & NetworkingUDP is a connectionless, message-oriented protocol with an 8-byte header. It provides no delivery guarantees, ordering, or error correction. Applications choose UDP when they need low latency (DNS, gaming), can tolerate loss (video streaming), or implement their own reliability (QUIC). Unlike TCP's byte stream, UDP preserves message boundaries — each send() is received as one datagram.
udpnetworkingX-Forwarded-For: Client IP Behind Proxies and the Trust Problem
•1 min read•Systems & NetworkingX-Forwarded-For carries a chain of IP addresses appended by each proxy that handles a request. The leftmost IP is the claimed client IP — but it can be spoofed. Backend applications must only trust IPs added by proxies they control. Trusting the wrong X-Forwarded-For position enables IP spoofing for rate limiting, geo-blocking, and access control bypass.
httpproxy