<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>kenura</title><link>https://kenura.krag.lk/</link><description>Recent content on kenura</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sun, 03 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://kenura.krag.lk/index.xml" rel="self" type="application/rss+xml"/><item><title>Bazel for Multi-Language Monorepos: One Build System for Go, C++, TypeScript, and WASM</title><link>https://kenura.krag.lk/posts/bazel-multilanguage-monorepo/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/bazel-multilanguage-monorepo/</guid><description>&lt;h2 id="whats-a-monorepo">What&amp;rsquo;s a Monorepo?&lt;/h2>
&lt;p>A &lt;strong>monorepo&lt;/strong> is a single git repository that holds multiple related projects. Instead of having separate repos for your backend, frontend, and shared libraries, everything lives together. You can see all the code, make cross-cutting changes in one commit, and ensure that related projects stay compatible.&lt;/p>
&lt;p>The challenge: different parts of the codebase use different languages and build tools. Go uses &lt;code>go build&lt;/code>. C++ uses &lt;code>cmake&lt;/code> or &lt;code>make&lt;/code>. TypeScript uses &lt;code>npm&lt;/code> and &lt;code>vite&lt;/code>. How do you tie these together into one coherent build?&lt;/p></description></item><item><title>Building a Multi-Screen TUI in Go with Bubbletea</title><link>https://kenura.krag.lk/posts/bubbletea-tui/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/bubbletea-tui/</guid><description>&lt;h2 id="what-is-a-tui">What Is a TUI?&lt;/h2>
&lt;p>A &lt;strong>TUI&lt;/strong> (Terminal User Interface) is an interactive application that runs in the terminal but behaves more like a desktop app — it has screens, navigation, live-updating panels, and keyboard shortcuts.&lt;/p>
&lt;p>Examples you&amp;rsquo;ve probably used: &lt;code>htop&lt;/code>, &lt;code>lazygit&lt;/code>, &lt;code>k9s&lt;/code>, &lt;code>ncdu&lt;/code>. These aren&amp;rsquo;t just text output — they&amp;rsquo;re full interactive applications that happen to live in your terminal.&lt;/p>
&lt;p>Go&amp;rsquo;s ecosystem has an excellent framework for building these: &lt;strong>Bubbletea&lt;/strong> (from Charm Bracelet). It uses the Elm architecture — a functional, message-driven model that makes complex TUIs manageable.&lt;/p></description></item><item><title>ClickHouse for Financial Data: Candles, Footprints, and the Timezone Trap</title><link>https://kenura.krag.lk/posts/clickhouse-financial-timeseries/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/clickhouse-financial-timeseries/</guid><description>&lt;p>When you&amp;rsquo;re building a trading dashboard, you need to answer queries like:&lt;/p>
&lt;ul>
&lt;li>&amp;ldquo;Give me all 1-minute candles for BTCUSDT between 9am and 5pm&amp;rdquo;&lt;/li>
&lt;li>&amp;ldquo;Show me the buy/sell volume breakdown at each price level for the last hour&amp;rdquo;&lt;/li>
&lt;li>&amp;ldquo;Enrich every candle with the open interest and funding rate at that time&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;p>These are &lt;strong>analytical&lt;/strong> queries — they aggregate millions of raw trades into summaries. ClickHouse is a database built specifically for this kind of work, and it handles these queries in milliseconds even on hundreds of millions of rows.&lt;/p></description></item><item><title>Docker, Docker Hub, GitHub Packages, and CI/CD Pipelines: The Complete Picture</title><link>https://kenura.krag.lk/posts/docker-cicd-github/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/docker-cicd-github/</guid><description>&lt;h2 id="what-is-docker-and-why-does-it-matter">What Is Docker and Why Does It Matter?&lt;/h2>
&lt;p>When you write code on your laptop, it works. When you deploy it to a server, it breaks. Different OS version, different library versions, different environment variables, different file paths. &amp;ldquo;It works on my machine&amp;rdquo; is one of the oldest problems in software.&lt;/p>
&lt;p>Docker solves this by packaging your application with everything it needs to run — the OS libraries, the runtime, the config — into a single &lt;strong>image&lt;/strong>. That image runs identically everywhere: your laptop, a CI server, a production server in a data center.&lt;/p></description></item><item><title>FlatBuffers and Protobuf in One System: Picking the Right Serialization Format</title><link>https://kenura.krag.lk/posts/flatbuffers-vs-protobuf-dual-protocol/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/flatbuffers-vs-protobuf-dual-protocol/</guid><description>&lt;h2 id="what-is-serialization">What Is Serialization?&lt;/h2>
&lt;p>When two programs communicate — a Go backend and a TypeScript frontend, or a Go server and a C++ desktop app — they need to send data over the network. But data in memory (structs, objects, arrays) can&amp;rsquo;t be sent directly over a socket. It needs to be converted to bytes first.&lt;/p>
&lt;p>That conversion is &lt;strong>serialization&lt;/strong>. The reverse — converting bytes back into usable data structures — is &lt;strong>deserialization&lt;/strong>.&lt;/p></description></item><item><title>Git + GitHub CLI: A Unified Terminal Workflow</title><link>https://kenura.krag.lk/posts/git-gh-productivity/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/git-gh-productivity/</guid><description>&lt;h2 id="two-tools-one-job">Two Tools, One Job&lt;/h2>
&lt;p>&lt;code>git&lt;/code> and &lt;code>gh&lt;/code> are different tools that work on the same codebase. Understanding the split helps you use them together effectively.&lt;/p>
&lt;p>&lt;strong>&lt;code>git&lt;/code>&lt;/strong> talks to your local repository and git remotes. It manages commits, branches, merges, stashes, history. Everything git does is about the version control state of your code.&lt;/p>
&lt;p>&lt;strong>&lt;code>gh&lt;/code>&lt;/strong> talks to the GitHub API. It manages pull requests, issues, GitHub Actions runs, releases, and repository settings. It&amp;rsquo;s essentially a CLI wrapper around GitHub&amp;rsquo;s web interface.&lt;/p></description></item><item><title>Go Error Handling: Patterns That Actually Work</title><link>https://kenura.krag.lk/posts/go-error-handling/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/go-error-handling/</guid><description>&lt;h2 id="why-go-errors-look-weird-to-beginners">Why Go Errors Look Weird to Beginners&lt;/h2>
&lt;p>If you&amp;rsquo;re coming from Python, JavaScript, or Java, Go&amp;rsquo;s error handling looks repetitive:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nf">doSomething&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">err&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nf">doSomethingElse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">result&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">err&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In other languages, exceptions handle this automatically — an error thrown anywhere in the call stack bubbles up to the nearest &lt;code>catch&lt;/code>. Go doesn&amp;rsquo;t have exceptions. Errors are just values returned from functions. You check them explicitly every time.&lt;/p></description></item><item><title>Monorepos: What They Are, Why You'd Want One, and When You Wouldn't</title><link>https://kenura.krag.lk/posts/monorepo-guide/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/monorepo-guide/</guid><description>&lt;h2 id="whats-a-monorepo">What&amp;rsquo;s a Monorepo?&lt;/h2>
&lt;p>A &lt;strong>monorepo&lt;/strong> (monolithic repository) is a single git repository containing multiple related projects. Instead of one repo per service or library, everything lives together:&lt;/p>
&lt;pre tabindex="0">&lt;code>my-company/
├── backend/ # Go service
├── frontend/ # React app
├── mobile/ # React Native app
├── shared/
│ ├── proto/ # Protobuf schemas
│ ├── types/ # Shared TypeScript types
│ └── utils/ # Shared utilities
├── infra/ # Terraform, Kubernetes configs
└── tools/ # Internal tooling
&lt;/code>&lt;/pre>&lt;p>One &lt;code>git clone&lt;/code>, and you have the entire codebase. One PR can touch backend + frontend + shared types + infra in a single commit.&lt;/p></description></item><item><title>One C++ App, Two Targets: ImGui on Vulkan and WebAssembly</title><link>https://kenura.krag.lk/posts/imgui-wasm-vulkan/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/imgui-wasm-vulkan/</guid><description>&lt;h2 id="the-problem-two-platforms-one-codebase">The Problem: Two Platforms, One Codebase&lt;/h2>
&lt;p>Most UI frameworks make you choose: build for desktop (native, fast) or build for web (JavaScript, runs everywhere). If you want both, you write it twice — once in C++, once in TypeScript/React — and then spend the next year keeping them in sync.&lt;/p>
&lt;p>There&amp;rsquo;s a different approach: write the application logic once in C++, and compile it to both targets. Native binary for desktop with Vulkan. WebAssembly module for the browser. Same source, different compilation backend.&lt;/p></description></item><item><title>Redis as a Real-Time Data Pipeline: Streams, Pub/Sub, and Hash Patterns</title><link>https://kenura.krag.lk/posts/redis-realtime-pipeline/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/redis-realtime-pipeline/</guid><description>&lt;h2 id="redis-isnt-just-a-cache">Redis Isn&amp;rsquo;t Just a Cache&lt;/h2>
&lt;p>Most developers know Redis as a key-value cache — put data in, get data out, set a TTL. But Redis has several data structures that make it a powerful backbone for real-time pipelines:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Streams&lt;/strong> — append-only log with consumer groups (like Kafka, but simpler)&lt;/li>
&lt;li>&lt;strong>Pub/Sub&lt;/strong> — fire-and-forget message broadcasting&lt;/li>
&lt;li>&lt;strong>Hashes&lt;/strong> — key-value maps with field-level gets and sets&lt;/li>
&lt;li>&lt;strong>Sorted Sets&lt;/strong> — ordered data with score-based queries&lt;/li>
&lt;/ul>
&lt;p>A real-time market data pipeline uses all of these, each for a different job. Understanding which tool fits which job is the key.&lt;/p></description></item><item><title>Schema-Driven Codegen: One File to Rule Your Database, SQL Views, and Go Structs</title><link>https://kenura.krag.lk/posts/schema-driven-codegen/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/schema-driven-codegen/</guid><description>&lt;p>Imagine you&amp;rsquo;re building a system that stores millions of financial trades in a database. You need three things that all describe the same data:&lt;/p>
&lt;ol>
&lt;li>A &lt;strong>database table&lt;/strong> — tells ClickHouse how to store the rows&lt;/li>
&lt;li>A &lt;strong>Go struct&lt;/strong> — lets your Go code read and write rows in a type-safe way&lt;/li>
&lt;li>A &lt;strong>SQL view&lt;/strong> — a stable &amp;ldquo;clean&amp;rdquo; layer your queries always read from&lt;/li>
&lt;/ol>
&lt;p>If you write all three by hand, they start identical. Six months later, someone renames a column in the database but forgets to update the struct. A type changes in the view but not in the Go code. These bugs are silent — everything compiles, but your data is wrong at runtime.&lt;/p></description></item><item><title>Self-Healing Data Pipelines: How to Build Systems That Fix Their Own Gaps</title><link>https://kenura.krag.lk/posts/self-healing-pipelines/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/self-healing-pipelines/</guid><description>&lt;p>Here&amp;rsquo;s the reality of running any system that collects real-time data: things will go wrong. Your WebSocket connection will drop at 3am. Your process will restart in the middle of a busy trading session. The network will hiccup for 30 seconds.&lt;/p>
&lt;p>When that happens, you miss data. The question is: does your system notice, and does it fix itself?&lt;/p>
&lt;p>A &amp;ldquo;self-healing&amp;rdquo; pipeline is one that can answer yes to both. This post explains how to build one.&lt;/p></description></item><item><title>Serving gRPC and HTTP from One Go Server with ConnectRPC</title><link>https://kenura.krag.lk/posts/grpc-connect-dual-server/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/grpc-connect-dual-server/</guid><description>&lt;h2 id="the-problem-with-standard-grpc-in-browsers">The Problem with Standard gRPC in Browsers&lt;/h2>
&lt;p>gRPC is excellent for service-to-service communication. It&amp;rsquo;s fast, strongly typed from a &lt;code>.proto&lt;/code> schema, and supports bidirectional streaming. But it has a fundamental limitation: &lt;strong>gRPC requires HTTP/2&lt;/strong>, and browsers don&amp;rsquo;t support HTTP/2 in a way that gRPC can use directly.&lt;/p>
&lt;p>The browser&amp;rsquo;s &lt;code>fetch&lt;/code> API and &lt;code>XMLHttpRequest&lt;/code> don&amp;rsquo;t expose the low-level HTTP/2 framing that gRPC needs. So a TypeScript frontend can&amp;rsquo;t call a standard gRPC server.&lt;/p></description></item><item><title>Taskfiles: A Better Makefile for Modern Projects</title><link>https://kenura.krag.lk/posts/taskfiles/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/taskfiles/</guid><description>&lt;h2 id="whats-a-task-runner">What&amp;rsquo;s a Task Runner?&lt;/h2>
&lt;p>When you work on a software project, there are repetitive commands you run all the time:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker compose up -d
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bazel build //backend:theron
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">./build/theron serve --collect
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>You could type these every time. Or you could write them down in a script. But scripts don&amp;rsquo;t give you dependency tracking (&amp;ldquo;run this only if that hasn&amp;rsquo;t been done&amp;rdquo;), parallel execution, or self-documentation.&lt;/p>
&lt;p>That&amp;rsquo;s what a &lt;strong>task runner&lt;/strong> does. The classic one is &lt;code>make&lt;/code> — you&amp;rsquo;ve probably seen &lt;code>make build&lt;/code> or &lt;code>make test&lt;/code>. But &lt;code>make&lt;/code> has decades of quirks and a syntax that trips up even experienced developers.&lt;/p></description></item><item><title>Three-Tier Data Collection: WebSocket, S3 Archives, and REST Gap Filling</title><link>https://kenura.krag.lk/posts/websocket-s3-rest-collection/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/websocket-s3-rest-collection/</guid><description>&lt;h2 id="the-problem-with-just-using-a-websocket">The Problem With Just Using a WebSocket&lt;/h2>
&lt;p>If you&amp;rsquo;re collecting market data, the most obvious approach is: connect to the exchange WebSocket, receive every trade as it happens, write it to the database. Simple.&lt;/p>
&lt;p>This works fine &lt;em>while your system is running&lt;/em>. But what about:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Historical data?&lt;/strong> The WebSocket only gives you data from now forward. How do you get the last 3 years?&lt;/li>
&lt;li>&lt;strong>Downtime?&lt;/strong> If your system was offline for 2 hours, you missed 2 hours of data. The WebSocket doesn&amp;rsquo;t replay missed messages.&lt;/li>
&lt;li>&lt;strong>Slow REST backfill?&lt;/strong> Downloading a year of trades via REST API (limited to 1000 rows per request) would take days.&lt;/li>
&lt;/ul>
&lt;p>The solution is a &lt;strong>three-tier architecture&lt;/strong>: each tier handles what it&amp;rsquo;s best at, and all tiers write to the same storage layer that automatically handles duplicates.&lt;/p></description></item><item><title>Using Make as a Task Runner: Beyond Just Compiling C</title><link>https://kenura.krag.lk/posts/make-task-runner/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/make-task-runner/</guid><description>&lt;h2 id="make-isnt-just-for-c">Make Isn&amp;rsquo;t Just for C&lt;/h2>
&lt;p>Most developers encounter &lt;code>make&lt;/code> in the context of compiling C or C++ code. The default tutorials show you how to compile &lt;code>.c&lt;/code> files into &lt;code>.o&lt;/code> files and link them into a binary.&lt;/p>
&lt;p>But &lt;code>make&lt;/code> is actually a general-purpose task runner. Any project — Go, Python, Node.js, a data pipeline — can benefit from a &lt;code>Makefile&lt;/code> as a collection of documented, runnable commands.&lt;/p>
&lt;p>You&amp;rsquo;ve probably already used it without thinking about it:&lt;/p></description></item><item><title>WebAssembly Deep Dive: C++, Emscripten, WebGPU, and the Browser's New Runtime</title><link>https://kenura.krag.lk/posts/webassembly-cpp-webgpu/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/posts/webassembly-cpp-webgpu/</guid><description>&lt;h2 id="what-is-webassembly">What Is WebAssembly?&lt;/h2>
&lt;p>WebAssembly (WASM) is a binary instruction format that runs in the browser at near-native speed. It&amp;rsquo;s not JavaScript, not a plugin, and not a virtual machine in the traditional sense. It&amp;rsquo;s a compact binary format that browsers can parse and execute much faster than JavaScript.&lt;/p>
&lt;p>Think of it this way: JavaScript is text that the browser interprets at runtime. WebAssembly is pre-compiled binary — closer to what your CPU actually executes. The browser still sandboxes it (it can&amp;rsquo;t access files or the OS directly), but within the browser sandbox, WASM code runs fast.&lt;/p></description></item><item><title>About</title><link>https://kenura.krag.lk/about/</link><pubDate>Thu, 01 Jan 2026 00:00:00 +0000</pubDate><guid>https://kenura.krag.lk/about/</guid><description>&lt;h2 id="kenura-r-gunarathna">Kenura R. Gunarathna&lt;/h2>
&lt;p>Founder &amp;amp; CEO at &lt;a href="https://krag.lk">KRAG&lt;/a>. Full-stack developer building websites, web apps, and software across the stack — from embedded systems to production web services. Currently also at &lt;a href="https://thenex.global">Thenex Global&lt;/a>.&lt;/p>
&lt;p>&lt;strong>Education&lt;/strong>&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Education&lt;/th>
 &lt;th>&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>University of Colombo&lt;/td>
 &lt;td>B.Sc. Electronics &amp;amp; IT Special Degree · Physics Dept. &lt;em>(Years 1–2: Physics, Applied Mathematics, Statistics, CS)&lt;/em>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Thurstan College, Colombo&lt;/td>
 &lt;td>Secondary · Grades 6–13&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Asoka College&lt;/td>
 &lt;td>Primary · Grades 1–5&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="skills">Skills&lt;/h2>
&lt;p>&lt;strong>Languages&lt;/strong>&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Level&lt;/th>
 &lt;th>&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Expert&lt;/td>
 &lt;td>HTML · CSS · JavaScript (ES6/ES7) · jQuery · PHP · SQL · Python · Go · C · Kotlin&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Proficient&lt;/td>
 &lt;td>C++ · Java · MongoDB&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Learning&lt;/td>
 &lt;td>TypeScript&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Frontend&lt;/strong> — React · Next.js · Astro.js · SolidJS · Svelte · Tailwind CSS · Bootstrap · Jetpack Compose · ImGui (C++)&lt;/p></description></item></channel></rss>