KPI Tracker — AI-Powered Client Performance Dashboard with Notion as the Entire Backend

This is a submission for the Notion MCP Challenge.

What I Built

KPI Tracker is an AI-powered client quarterly performance dashboard where Notion is the entire backend — no traditional database, no SQL, no Redis. Just Notion, connected through the official Model Context Protocol (MCP) server.

The problem it solves: consultants, analysts, and account managers who track multiple clients’ performance currently spend hours manually copy-pasting data from quarterly reports. KPI Tracker reduces that to under 2 minutes.

How it works

  1. Paste a URL or drag-and-drop a PDF — an earnings press release, 10-K, 10-Q, or any financial report
  2. AI extracts all KPIs automatically — Revenue, Gross Margin, Net Margin, EPS, Operating Income, Customer Count, and more
  3. Human-in-the-loop review — you see every extracted KPI with its confidence score before anything saves
  4. One click saves everything to Notion — the app creates a client record, a quarterly report entry, and individual KPI rows via Notion MCP
  5. Live dashboard renders 11+ interactive charts — all reading directly from Notion

Key features

  • Multi-Model AI — choose from 5 models at runtime: Gemini 2.5 Flash, Gemini 2.5 Pro, Claude Haiku 4.5, Sonnet 4.5, or Sonnet 4.6. No restart needed.
  • AI Revenue Forecasting — trend-based next-quarter revenue predictions per client, with reasoning
  • 11+ Chart Types — Revenue Trend (Area), Revenue Forecast (Bar), Margin Trends (Line), EPS Trends (Line), Segment Revenue (Stacked Bar), KPI Distribution (Pie), Confidence Distribution (Pie), QoQ Heatmap, Revenue vs Margins (Composed), KPI Gauges (Horizontal Bar), KPI Landscape (Treemap)
  • Drag-and-Drop Charts — reorder any chart by dragging; order persists in localStorage across sessions
  • Pin KPI Charts — click any KPI in the data table to view its historical trend, then pin it as a new chart on the dashboard
  • Ask AI — query your data in natural language (“Which client had the highest revenue growth?”)
  • Custom KPIs — manually add metrics the AI missed
  • Smart $B Formatting — revenue values ≥ $1,000M auto-display as billions (e.g., $12.7B)
  • All Clients Aggregation — summary cards intelligently aggregate across all clients (Revenue/OpIncome summed, margins/EPS averaged)
  • PDF + URL Ingestion — works with public URLs or local files
  • Secure — API keys never touch the browser; prompt injection detection built-in

Tech stack

Layer Technology
Framework Next.js 15.1 (App Router)
Frontend React 19, Tailwind CSS 3.4
Charts Recharts 2.13 (11+ chart types)
Drag-and-Drop @dnd-kit/core + @dnd-kit/sortable
AI (default) Google Gemini 2.5 Flash
AI (alternative) Anthropic Claude Haiku / Sonnet
Backend/DB Notion (via MCP + direct SDK)
MCP @modelcontextprotocol/sdk 1.27, @notionhq/notion-mcp-server 2.2
PDF Parsing pdf-parse
Deployment Vercel

Demo

Static demo (no API keys needed): notion-kpi.sasrath.com

Show us the code

GitHub logo

sasrath
/
notion-kpi-tracker

Turn any quarterly report URL into a live KPI dashboard. Powered by Claude/Gemini AI + Notion MCP. Zero traditional database.

📊 KPI Tracker — Powered by Notion MCP

AI-powered client quarterly performance tracker with multi-model support (Google Gemini + Anthropic Claude), 11+ interactive charts, AI revenue forecasting, and Notion as the sole backend. Zero traditional database.

Built for the dev.to Notion MCP Challenge

✨ Features

  • 🤖 AI Report Ingestion — paste a URL or drag-and-drop a PDF, AI extracts KPIs automatically
  • 📊 Interactive Dashboard — 11+ chart types (Area, Bar, Line, Pie, Heatmap, Treemap, Composed & more)
  • 🔮 AI Revenue Forecasting — trend-based next-quarter revenue predictions per client
  • 🧠 Multi-Model AI — choose from 5 models: Gemini 2.5 Flash/Pro, Claude Haiku 4.5, Sonnet 4.5/4.6
  • ✋ Human-in-the-Loop — review and edit extracted KPIs before anything saves to Notion
  • 🔄 Drag-and-Drop Charts — reorder dashboard charts by dragging; order persists across sessions
  • 📌 Pin KPI Charts — click any KPI in the table to view its trend, then pin it to the…

The full source is on GitHub: github.com/sasrath/notion-kpi-tracker

Architecture at a glance

User Browser (React SPA)
    │
    ├── Google Gemini API   (web search, extraction, forecasts)
    ├── Anthropic Claude API (extraction, queries, forecasts)
    │
    └── Next.js API Routes
            │
            └── lib/notion-mcp.js (MCP Client singleton)
                    │ (stdio)
                    └── @notionhq/notion-mcp-server (subprocess)
                            │ (HTTP)
                            └── Notion API → 3 Databases

The MCP server runs as a long-lived subprocess managed as a singleton. It starts on the first API call and stays alive for subsequent requests. The callNotionTool() helper handles JSON parsing, error detection, and auto-reconnect.

Key files

  • app/page.jsx — Main dashboard: 11+ charts, DnD grid, KPI table, saving progress, tab persistence
  • lib/notion-mcp.js — MCP client singleton with auto-reconnect and 21-tool mapping
  • lib/llm.js — Multi-model AI layer (Gemini + Claude), safeParseJSON for truncated responses
  • lib/transforms.js — 19 data transform functions for chart preparation
  • setup-notion.mjs — Automated Notion database setup script

How I Used Notion MCP

Notion is not a side feature — it is the backend. Every data operation flows through the official Notion MCP server (@notionhq/notion-mcp-server). There is zero traditional database code anywhere in the project.

The three-database schema

The app uses three relational Notion databases:

Clients — one row per company (Name, Industry, Website, Status)

Quarterly Reports — one row per earnings report, linked to a Client (Quarter, Year, Report URL, Raw Summary)

KPIs — one row per metric, linked to both a Report and a Client (KPI Name, Value, Unit, Quarter, Year, Source, Confidence, Notes)

MCP operations in action

What the app does MCP Tool used
Save a new client, report, or KPI API-post-page
Load all clients / search KPIs API-post-search
Delete (archive) a KPI row API-patch-page
Verify Notion connection on startup API-get-self
Check database schema API-retrieve-a-database

Here’s how the MCP connection is established in lib/notion-mcp.js:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

const transport = new StdioClientTransport({
  command: "npx",
  args: ["-y", "@notionhq/notion-mcp-server"],
  env: {
    ...process.env,
    OPENAPI_MCP_HEADERS: JSON.stringify({
      Authorization: `Bearer ${process.env.NOTION_API_KEY}`,
      "Notion-Version": "2022-06-28",
    }),
  },
});

const client = new Client({ name: "kpi-tracker", version: "1.0.0" });
await client.connect(transport);

Every read and write — from loading the dashboard to saving AI-extracted KPIs to deleting a row — goes through this MCP connection. The app treats Notion exactly like a traditional database, but through the standardized MCP protocol.

Why MCP over the direct Notion SDK?

Using MCP means the app communicates through a standardized protocol layer. This gives us:

  1. Tool discovery — the MCP server exposes 21 tools that map to Notion’s REST API; the app discovers them at startup
  2. Protocol standardization — the same MCP client pattern works with any MCP-compatible service
  3. Decoupled auth — credentials flow through the MCP transport layer, not hardcoded in API calls

The result

The entire data lifecycle — AI extraction → human review → save to Notion → read for dashboard → AI forecasting — runs through Notion MCP. No separate database server to manage, no migrations to run, no connection pools to configure. Just Notion.

🔐 Full Demo Access

The public demo at notion-kpi.sasrath.com shows sample data to protect API limits.

For the full live experience with real Notion + Gemini:
👉 notion-kpi.sasrath.com/judges

Pre-loaded with real earnings data from Apple,
Microsoft and Intel Q4 2025.

About Me

I’m a consultant focused on practical AI integrations, data tooling, and building small, maintainable web apps that solve real problems. I publish code, demos, and write-ups so others can learn and contribute — feedback, issues, and pull requests are very welcome.My Github (source: https://github.com/sasrath).

How to Handle Sensitive Data Securely in Terraform

The Three Ways Secrets Leak (And How to Stop Every One)

Day 13 of the 30-Day Terraform Challenge — and today I learned that even when you think your secrets are safe, they’re probably not.

Secrets leak in Terraform in three predictable ways. I found all three. I fixed all three. And I documented what I learned so you don’t have to make the same mistakes.

The Three Leak Paths

Leak Path 1: Hardcoded in .tf Files

The mistake:

resource "aws_db_instance" "example" {
  username = "admin"
  password = "super-secret-password"  # ❌
}

This password is now in your Git history. Forever. Even if you delete it, it’s still in the commit history. Anyone with access to your repo can see it.

The fix:

variable "db_password" {
  type      = string
  sensitive = true
  # No default — Terraform will prompt
}

Now the password never touches your code.

Leak Path 2: Variable Defaults

The mistake:

variable "db_password" {
  default = "super-secret-password"  # ❌ Still in code
}

Default values are stored in your .tf files. Same problem as hardcoding. The secret is right there in version control.

The fix: No defaults for secrets. Ever.

Leak Path 3: The State File

The reality: Even if you fix the first two, Terraform stores every resource attribute in terraform.tfstate in plaintext.

$ cat terraform.tfstate | grep password
"password": "MySecurePassword123!"  # ❌ Right there!

Anyone with read access to the state file can see all your secrets.

The fix:

  • Use remote state with encryption (S3 + encrypt = true)
  • Restrict access with IAM policies
  • Never commit state to Git
  • Enable versioning to recover from mistakes

AWS Secrets Manager: The Right Way

Instead of hardcoding, store secrets in AWS Secrets Manager:

data "aws_secretsmanager_secret" "db_credentials" {
  name = "prod/db/credentials"
}

data "aws_secretsmanager_secret_version" "db_credentials" {
  secret_id = data.aws_secretsmanager_secret.db_credentials.id
}

locals {
  db_credentials = jsondecode(
    data.aws_secretsmanager_secret_version.db_credentials.secret_string
  )
}

resource "aws_db_instance" "example" {
  username = local.db_credentials["username"]
  password = local.db_credentials["password"]
}

What this gives you:

  • ✅ Secrets never appear in your .tf files
  • ✅ Fetched at runtime
  • ✅ Centralized secret management
  • ✅ Rotation capabilities

Marking Secrets as Sensitive

Terraform’s sensitive = true prevents secrets from appearing in terminal output:

output "db_password" {
  value     = local.db_credentials["password"]
  sensitive = true
}

Plan output:

db_password = <sensitive>

⚠️ Important: This does NOT prevent secrets from being stored in state. It only hides them from the terminal.

State File Security Checklist

After deploying, I verified all of these:

✅ S3 backend with encryption:

terraform {
  backend "s3" {
    bucket  = "my-terraform-state"
    encrypt = true  # AES-256 encryption
  }
}

✅ Block public access on S3 bucket

✅ Enable versioning to recover from mistakes

✅ Restrict IAM policy to only Terraform runners

✅ .gitignore to prevent accidental commits:

.terraform/
*.tfstate
*.tfstate.backup
*.tfvars

What the Plan Looked Like

When I ran terraform plan, the output showed:

username = (sensitive value)
password = (sensitive value)
db_password = (sensitive value)

The actual secrets never appeared in my terminal. But when I checked the state file:

$ cat terraform.tfstate | grep password
"password": "MySecurePassword123!"

The password was right there in plaintext. This was the “aha!” moment — state encryption isn’t optional. It’s mandatory.

Chapter 6 Learnings

Does sensitive = true prevent secrets from being stored in state?
No. It only hides them from terminal output. Secrets are still in state. You must secure the state file.

Vault vs Secrets Manager:

  • AWS Secrets Manager: AWS-native, simpler, good for AWS-only environments, integrates with RDS
  • HashiCorp Vault: Multi-cloud, dynamic secrets, fine-grained policies, more complex to set up

Why secrets appear in state: Terraform stores all resource attributes to track infrastructure. The only solution is to secure the state file itself with encryption and access controls.

What I Learned

Secrets management isn’t just about not hardcoding passwords. It’s about:

  1. Using a secrets manager — AWS Secrets Manager, HashiCorp Vault
  2. Never hardcoding — no passwords in .tf files, no defaults
  3. Marking sensitive outputs — sensitive = true hides from terminal
  4. Securing the state file — encrypted S3 backend, restricted access
  5. Proper .gitignore — never commit state or tfvars

The state file is the last line of defence. Secure it like you would a password manager.

My .gitignore

# Terraform
.terraform/
.terraform.lock.hcl
*.tfstate
*.tfstate.backup
*.tfvars
*.tfvars.json

# Local secrets
*.secret
secrets.tfvars

# Environment
.env
.env.local

The Bottom Line

Three leak paths. Three fixes:

Leak Path Fix
Hardcoded in .tf Use variables with sensitive = true
Variable defaults Remove defaults, use Secrets Manager
State file Encrypted remote backend, restricted access

Security isn’t optional in production infrastructure.

Resources:

P.S. The moment I saw my password in the state file was humbling. No matter how careful you are with your code, if you don’t secure your state, you’re leaving secrets in plain sight.

SAGE — A Notion MCP Academic Co-Pilot for Filipino University Students

This is a submission for the Notion MCP Challenge

What I Built

SAGE — Student Agent for Guided Education — is an academic co-pilot built for Filipino university students. You connect your Notion account, tell it your program, year, and semester, and it builds your entire academic workspace for you: course pages, topic breakdowns, task databases, and study prompts, all structured and ready to go.

It’s built on Notion MCP, FastAPI, Supabase, and Qwen on Vultr Inference. The workspace it creates is seeded with real curriculum data from Ghost Commons, a pre-built registry covering all six BSCPE Year 2 Semester 1 courses — Differential Equations, Engineering Data Analysis, Circuits, and more.

The frustration behind it is real: as a computer engineering student in the Philippines, my tasks come from Teams, Google Classroom, Canvas, and Blackboard all at once. I forget things. I don’t know where to start. SAGE is the one place I wished existed.

Video Demo

Show us the code

github.com/kuya-carlo/sage-mcp

How I Used Notion MCP

Notion MCP is the core of SAGE — not a wrapper, not an afterthought. Every workspace action goes through it: creating the root page, spinning up course databases with full property schemas, inserting topic rows, and querying workspace state for the load monitor.

The hardest part was making it stable at scale. Building a full semester workspace means dozens of concurrent MCP calls — course pages, Topics databases, Tasks databases, individual topic entries — all in parallel. I implemented a global semaphore, persistent SSE connection pooling via an async context manager, and inter-course delays to keep the MCP server stable.

What Notion MCP unlocks here is genuine multi-tenant isolation: every student authorizes their own Notion workspace via OAuth, and SAGE writes only to the pages they explicitly grant access to. Tokens are AES-encrypted at rest. No shared state, no data leakage.

The result is a workspace that feels like it was built by hand — because structurally, it was. SAGE just does it in one shot.

Local-First Software Is Winning: A Developer Guide to Building Without the Cloud

I lost two hours of work last Tuesday because my internet went down for eleven minutes.

Not because I was using some exotic cloud-dependent tool. I was editing a document in a mainstream SaaS product. The kind with a billion-dollar valuation and a “works offline” checkbox somewhere in their marketing materials. When my connection dropped, the app froze. When it came back, my changes were gone. The conflict resolution had decided that the empty server state was more recent than my local edits.

Eleven minutes of downtime. Two hours of work. And a growing suspicion that maybe the way we build software has a fundamental problem.

I am not the only one thinking this. Local-first software, a paradigm where your data lives on your device first and syncs to the cloud second, has been gaining serious momentum throughout 2026. FOSDEM dedicated an entire developer room to local-first, CRDTs, and sync engines this year. Companies like Linear and Figma have proven that local-first principles can power products used by millions. And the developer tooling has finally matured to the point where you do not need a PhD in distributed systems to build this way.

Here is what local-first actually means, why it matters now, and how to start building with it.

What Local-First Actually Means

The term “local-first” comes from a 2019 research paper by Ink & Switch, a lab focused on the future of computing. Their definition is straightforward: local-first software keeps a full copy of your data on your device. The cloud exists for sync and backup, not as the source of truth.

This is not the same as “offline mode.” Most apps treat offline as a degraded state. You get a warning banner. Some features stop working. When you reconnect, the app tries to reconcile what happened while you were offline, and sometimes it gets that reconciliation wrong.

Local-first flips this entirely. Your device is the primary. Reads and writes happen locally, instantly, with zero network latency. The network is secondary. When it is available, your changes sync to other devices and to a server. When it is not, you keep working normally. There is no degraded state because local is the default state.

The seven principles from the original Ink & Switch paper still hold:

  1. No spinners. Operations happen locally, so they feel instant.
  2. Your data is yours. It lives on your device in a format you control.
  3. The network is optional. Everything works offline. Sync happens when possible.
  4. Collaboration works. Multiple people can edit the same data, and conflicts resolve automatically.
  5. Data outlives the software. Your data is stored in open formats, not locked in a proprietary cloud.
  6. Security and privacy by default. Data does not have to leave your device unless you choose to share it.
  7. Full user control. You decide where your data goes and who can access it.

If this sounds idealistic, consider that some of the most successful products of the past few years already follow these principles. Figma handles real-time collaboration with local-first techniques under the hood. Linear stores data locally and syncs in the background. Obsidian keeps everything as plain Markdown files on your machine. These are not niche experiments. They are products that millions of developers and designers use daily.

Why Local-First Is Gaining Ground in 2026

Three things have converged to make local-first practical this year in ways it was not before.

Cloud Fatigue Is Real

The cloud solved enormous problems. It also created new ones. Monthly SaaS bills that compound across dozens of tools. Vendor lock-in that makes switching feel impossible. Outages that take down your entire workflow because a server in Virginia had a bad day. And a growing awareness, especially in Europe with stricter data regulations, that sending every keystroke to someone else’s computer is not always a great idea.

Developers are feeling this acutely. If you are building a product and every feature requires a round trip to a server, your infrastructure costs scale linearly with usage. If you are building local-first, most computation happens on the user’s device. Your server costs drop dramatically because the server is doing sync, not processing.

The Tooling Has Matured

Two years ago, building a local-first app meant rolling your own sync layer. You needed deep knowledge of distributed systems, conflict resolution, and data replication. The developer experience was rough.

That has changed. Tools like PowerSync, ElectricSQL, Zero, and Triplit have turned what used to be a research project into a framework you can npm install. The abstractions are solid. The documentation is real. You can build a local-first app today with the same level of effort as building a traditional client-server app.

Users Expect Instant

We have trained users to expect instant responses. Every major app invests heavily in perceived performance. Skeleton screens, optimistic updates, background prefetching. All of these are workarounds for the fundamental latency of client-server architecture.

Local-first eliminates the need for these workarounds. When your reads and writes happen locally, the UI is genuinely instant. Not “feels instant because we show a skeleton.” Actually instant. Zero milliseconds of latency for every interaction. Users notice the difference even if they cannot articulate why the app “feels fast.”

CRDTs: The Technology That Makes It Work

The hardest problem in local-first is this: if two people edit the same data on different devices while offline, what happens when they reconnect?

Traditional databases solve this with locks or “last write wins.” One person’s changes overwrite the other’s. That is fine for a banking system where you want strict ordering. It is terrible for a collaborative document where both people’s changes matter.

Conflict-free Replicated Data Types, or CRDTs, solve this differently. They are data structures designed from the ground up to handle concurrent edits without conflicts. The math guarantees that no matter what order updates are received, every device eventually converges on the same state. No conflicts. No data loss. No central server needed to mediate.

Here is the simplest way to think about it. Imagine two people have a shared shopping list. Person A adds “milk” while offline. Person B adds “eggs” while offline. When they reconnect, a CRDT-based list will have both “milk” and “eggs.” Neither change is lost, and no conflict resolution was needed. The data structure itself handles it.

This works for more complex scenarios too. CRDT implementations exist for text documents (collaborative editing like Google Docs), JSON-like objects (application state), counters, sets, maps, and more. Libraries like Automerge and Yjs have been battle-tested in production for years and handle edge cases that would take months to solve from scratch.

The practical implication for you as a developer: you do not need to understand the mathematics behind CRDTs to use them. The libraries abstract the complexity. You interact with what feels like a normal JavaScript object, and the sync layer handles replication and conflict resolution behind the scenes.


const repo = new Repo({
  network: [new BrowserWebSocketClientAdapter('wss://sync.example.com')],
})

// Create a document that syncs automatically
const handle = repo.create({ items: [], title: 'Shopping List' })

// Local changes sync to all connected peers
handle.change((doc) => {
  doc.items.push({ name: 'milk', added: Date.now() })
})

That is it. You create a document, make changes locally, and the library handles syncing those changes to every connected device. No manual conflict resolution. No server-side merge logic. The CRDT guarantees convergence.

The Current Tool Landscape

The local-first ecosystem in 2026 has matured significantly. Here are the tools worth knowing about, organized by what they do best.

Sync Engines (Bring Your Own Backend)

PowerSync is the most production-ready option if you have an existing Postgres, MongoDB, or MySQL database. It syncs a subset of your server data to a local SQLite database on the client. Your backend stays the same. You add PowerSync as a sync layer between your existing database and your frontend. This is the lowest-friction path for teams migrating an existing app toward local-first.

ElectricSQL takes a similar approach but focuses specifically on Postgres. It provides active-active sync between your Postgres database and SQLite on the client. If your stack is already Postgres-based, ElectricSQL gives you local-first capabilities without changing your data model.

Full-Stack Local-First Frameworks

Zero and Triplit are designed for building collaborative apps from scratch. They handle the entire stack: local storage, sync, conflict resolution, and real-time updates. If you are building something like a Notion clone or a project management tool, these frameworks give you multiplayer collaboration out of the box.

Jazz takes an opinionated approach to local-first, providing a framework where collaborative data structures are the foundation of your app. It is particularly good for apps where real-time collaboration is a core feature, not an afterthought.

CRDT Libraries

Automerge is the gold standard for document-level CRDTs. It handles complex nested data structures and has excellent support for undo/redo, branching, and merging. If you are building something with rich, structured data (think: a collaborative spreadsheet or design tool), Automerge gives you the most flexibility.

Yjs is the go-to for text editing. It powers the real-time collaboration in many popular editors and has a massive ecosystem of bindings for frameworks like ProseMirror, TipTap, CodeMirror, and Monaco. If your app involves collaborative text editing, Yjs is likely the right choice.

Local Databases

SQLite (via wa-sqlite or better-sqlite3) is the local database that powers most of these solutions. Running SQLite in the browser via WebAssembly has become reliable and performant. On mobile and desktop (Electron, Tauri), native SQLite gives you a proper relational database on the user’s device.

RxDB provides a reactive, local-first database with built-in replication to any backend. It is framework-agnostic and works across browser, Node.js, React Native, and Electron.

When Local-First Makes Sense (and When It Does Not)

Local-first is not the right architecture for everything. Here is a practical framework for deciding.

Local-First Is a Strong Fit When:

Your app handles user-generated content. Note-taking apps, design tools, writing tools, project management, personal databases. Anything where the user creates and owns data benefits enormously from local-first. The data belongs to the user conceptually, so it makes sense for it to live on their device.

Offline capability matters. Field service apps, mobile apps used in areas with poor connectivity, tools for travelers. If your users cannot always count on a stable internet connection, local-first gives them a seamless experience regardless.

Real-time collaboration is a core feature. CRDTs were designed for exactly this. If multiple people need to edit the same data simultaneously, local-first with CRDTs gives you collaboration that works even when participants are intermittently offline.

You want to reduce server costs. When most computation happens on the client, your server does sync and backup rather than processing every request. This can dramatically reduce infrastructure costs, especially for apps with many concurrent users. For indie hackers and solo founders watching their SaaS costs, this matters.

Local-First Is Not Ideal When:

You need a single source of truth with strict consistency. Financial transactions, inventory systems, booking platforms. Anything where two people should not be able to “claim” the same resource simultaneously. CRDTs provide eventual consistency, not strong consistency.

Your data is primarily server-generated. Analytics dashboards, news feeds, search engines. If the server produces the data and the client just displays it, local-first adds complexity without much benefit.

Your app depends on real-time server-side processing. Live video streaming, multiplayer gaming with server authority, real-time bidding. These need a server in the loop for every interaction.

Regulatory requirements mandate server-side control. Some industries require that data be stored and processed on controlled servers. Local-first can complicate compliance in these cases.

Getting Started: A Practical Path

If you want to try local-first without rewriting your entire app, here is a pragmatic approach.

Start with One Feature

Pick a feature in your app where offline capability would genuinely improve the experience. A note editor, a task list, a settings panel. Something self-contained.

Build that one feature with a local-first approach. Use PowerSync or ElectricSQL to sync it with your existing database. Keep everything else the same. This gives you real experience with the paradigm without a risky full rewrite.

Choose Your Sync Strategy

The biggest architectural decision is how data moves between devices. You have three main options:

Server-mediated sync is the most familiar. Clients sync through your server. The server stores the canonical copy and mediates sync between devices. This is what PowerSync and ElectricSQL do. It is the easiest to reason about and fits naturally with existing backend architectures.

Peer-to-peer sync removes the server from the sync path. Devices communicate directly, often using WebRTC. This gives you true decentralization but adds complexity around discovery (how do devices find each other?) and availability (what if the peer you need to sync with is offline?).

Hybrid combines both. Sync through a server when available, fall back to peer-to-peer when not. This gives you the reliability of server-mediated sync with the resilience of P2P. Most production local-first apps end up here.

Handle the Hard Parts

Two things trip up developers new to local-first:

Schema changes. When your data model evolves, you need to handle migrations on every client device, not just on the server. Plan for this from the start. Use versioned schemas and write migration logic that runs locally.

Authorization. In a traditional app, the server checks permissions on every request. In a local-first app, the data is already on the client before the server can say no. You need to think about authorization differently. Typically, the sync layer handles this by controlling which data subsets sync to which clients.

What This Means for the Future of Web Development

I have been watching the JavaScript ecosystem evolve for years, and local-first feels like one of the more significant shifts. Not because it replaces everything, but because it changes the default assumption.

For a decade, the default assumption was: data lives on the server, the client asks for it. Local-first flips that to: data lives on the client, the server helps distribute it.

This is not theoretical. It changes how you think about building products. You stop worrying about API latency because there is no API call for local reads. You stop worrying about loading states because the data is already there. You stop worrying about offline because offline is just the normal state with sync paused.

The ecosystem is still young compared to traditional client-server tooling. You will hit rough edges. Documentation sometimes assumes distributed systems knowledge. Some tools are better suited for greenfield projects than migrations. But the trajectory is clear. Every major sync engine and CRDT library shipped significant improvements in the first quarter of 2026 alone.

If you are building a new product this year, especially anything involving user-generated content or collaboration, local-first deserves serious consideration. The tools are ready. The developer experience is good. And your users will notice the difference the first time they lose internet and your app keeps working like nothing happened.

Resources to Go Deeper

If you want to explore further, here are the starting points I found most useful:

The original Ink & Switch paper (“Local-First Software: You Own Your Data, in Spite of the Cloud”) is still the best introduction to the philosophy. It is long but worth reading in full.

lofi.so maintains a directory of local-first projects, tools, and frameworks. It is the most comprehensive catalog of the ecosystem.

The FOSDEM 2026 Local-First track recordings cover everything from beginner introductions to advanced CRDT implementations. The talks on sync engine internals are particularly good if you want to understand what these tools do under the hood.

And if you want to see local-first in action, clone one of the example apps from Automerge, Yjs, or Zero. Build a simple collaborative todo list. Watch two browser tabs edit the same data. Disconnect one, make changes on both, reconnect, and watch them merge without conflicts. That moment when it just works is when local-first clicks.

The cloud is not going away. But the assumption that every app needs to be cloud-first? That is already changing. And for the developers building the next generation of tools, that shift opens up possibilities that the always-online era never could.

Ritual Protocol: a key as an action, not an object

Ritual Protocol: a key as an action, not an object

Every secret storage system is only as secure as the storage itself. Someone steals your password database — they steal everything. Someone compromises your keystore — they compromise everything. You can encrypt the storage, split keys, add authentication factors — but the fundamental problem remains: the key exists as an object, and objects can be stolen.

Ritual Protocol takes a different approach. The key is not stored. It is reproduced through a sequence of actions — a ritual. Same actions, same parameters — same 32 bytes out. Every time. No storage — no attack vector tied to its compromise.

How it works

A ritual is an ordered set of rites. Each rite has a type and parameters. Order matters: sequences [A, B, C] and [C, B, A] produce different keys.

Rite types

V1 defines six rite types. They can be combined in any order and repeated — each repetition increases entropy.

The rites are intentionally varied — a text string, a symbol sequence, a file slice, a star map, a city and time, runes on a grid. The idea is that every user has their own natural way to create a unique and reproducible ritual. Some will remember a symbol sequence, some will use a file and an offset, some will use a city and time combination with personal meaning.

Full description of each type with entropy formulas is in the specification.

Finalization

And that’s really all there is to it. “I like it. Simple, easy to remember.” — J.S.

Each rite is hashed independently through SHA256, then the hashes are folded sequentially — rite order affects the result, this is an intentional property of the protocol.

The final state is run through three KDFs:

Argon2id (64 MB, t=3, p=4) → scrypt (128 MB) → BLAKE2b-256

Three algorithms were chosen deliberately — each has a different memory access pattern. This makes it harder to build specialized hardware for brute force. Each brute force attempt costs as much memory and time as a legitimate finalization.

At 80 bits of ritual entropy, the effective security is approximately 126 bits — the difference is the computational cost of the KDF chain.

What’s next

The protocol is intentionally minimal — exactly 32 bytes out and nothing more. What to do with them is the environment’s concern. The reference implementation in Go compiles to .dll/.so with a simple C API, which makes it easy to plug the core into almost anything. There’s a pretty decent demo in the examples.

Licensing: the author, as a responsible member of the open source community, hereby contributes this humble piece to the betterment of mankind. Personal use is free. If you’re building a business on it — you know where to find us.

GitHub: https://github.com/runetcom2014/ritual-protocol
Specification: https://zenodo.org/records/19090391

JNBridgePro vs IKVM vs Javonet: Java/.NET Bridge Comparison (2026)

I’ve been deep in the Java/.NET integration space for years, and the one question that comes up more than anything: which bridge tool should I actually use? IKVM? JNBridgePro? Javonet? Something else entirely?

The answer depends on your Java version, deployment model, and how much you value long-term support. Here’s an honest breakdown of all three — plus alternatives you should know about.

Why Compare Java/.NET Bridges?

Java/.NET bridges solve a common enterprise problem: your organization runs both Java and .NET codebases, and they need to communicate. Instead of rewriting code or building REST/gRPC wrappers, a bridge enables direct method calls between the two runtimes.

But bridges differ dramatically in architecture, performance characteristics, Java version support, and long-term viability. Choosing the wrong one can mean a forced migration later — exactly the kind of disruption you’re trying to avoid.

Quick Comparison Table

Feature JNBridgePro IKVM Javonet
Architecture In-process bridge (JVM + CLR) Bytecode translation (Java → CIL) Cross-runtime invocation
Java Version Support Java 8–21+ Java SE 8 only Java 8+
.NET Support Framework + Core/.NET 8/9 Framework, partial Core Framework + Core
Uses Real JVM? Yes No (translates to .NET) Yes
Latency per Call Microseconds Zero (native .NET) Low (in-process)
Dynamic Class Loading Full support Limited Supported
Reflection Support Full Partial Full
Commercial Support Professional (since 2001) None (community) Commercial
Linux / Docker / K8s Yes Partial Yes
License Commercial (free eval) Open source (MIT) Commercial ($69/mo+)
First Release 2001 2004 2015
Languages Supported Java + .NET Java → .NET only 6+ languages

JNBridgePro: In-Depth Review

How It Works

JNBridgePro runs a real JVM alongside the .NET CLR, either in the same process (shared memory) or connected via TCP. A proxy generation tool inspects your Java JARs and creates matching .NET proxy classes. Your C# or VB.NET code calls these proxy methods with native syntax — the bridge handles type conversion, exception marshaling, and memory management transparently.

Strengths

  • Full Java compatibility — Runs a real JVM, so any Java library, framework, or feature works. Virtual threads, records, pattern matching, dynamic proxies — all supported.
  • Mature and battle-tested — In production since 2001. Used by Fortune 500 companies in financial services, healthcare, manufacturing, and government.
  • Professional support — Guaranteed response times, dedicated engineering support, compatibility updates for new Java and .NET versions.
  • Flexible deployment — Shared memory (lowest latency), TCP (separate machines), Docker containers, Kubernetes pods.
  • Low integration effort — Proxy generation is automated. Most integrations go from evaluation to production in weeks, not months.

Limitations

  • Commercial license required (free evaluation available)
  • JVM overhead (typically 256MB–1GB heap)
  • Cross-runtime call overhead in microseconds — only matters in extremely tight loops

Best For

Enterprise teams that need reliable, long-term Java/.NET integration with professional support. Especially strong for modern Java (11+), containerized deployments, and compliance-grade reliability.

IKVM: In-Depth Review

How It Works

IKVM translates compiled Java bytecode (.class and .jar files) into .NET CIL assemblies. The translated Java code runs directly on the CLR as native .NET code — no JVM needed at runtime.

Strengths

  • Zero cross-runtime overhead — After translation, Java code IS .NET code. Native CLR calls.
  • No JVM dependency at runtime — Simplifies deployment.
  • Open source (MIT license) — Free for any use, including commercial.
  • Simple for simple cases — Self-contained Java libraries can work well.

Limitations

  • Java SE 8 only — Cannot use Java 9+ features (modules, records, virtual threads, sealed classes). This is the critical limitation.
  • Incomplete API coverage — Libraries depending on JVM internals, custom class loaders, or reflection-heavy frameworks may fail.
  • No commercial support — Community-maintained with periods of dormancy.
  • No dynamic class loading — Frameworks like Spring may not work.

Best For

Projects using simple, self-contained Java 8 libraries where open-source licensing is required and the limitations are acceptable. Not recommended for enterprise production systems needing long-term support.

Javonet: In-Depth Review

How It Works

Javonet provides a cross-runtime invocation framework supporting Java, .NET, Python, Ruby, Perl, and Node.js. It uses an invoke-based API where you specify class names and method names as strings.

Strengths

  • Multi-language support — Covers 6+ languages with one tool.
  • Modern Java support — Works with current Java versions.
  • Commercial support available — Paid plans with SLA options.
  • Cloud and container friendly — Modern deployment patterns supported.

Limitations

  • Invoke-based API — You call methods by string name rather than typed proxy classes. No compile-time type checking.
  • Generic approach — May not be as deeply optimized for Java/.NET as a purpose-built bridge.
  • Subscription pricing — Starts at $69/month, scales with usage.
  • Younger product — Founded 2015 vs JNBridgePro’s 2001.

Best For

Teams in polyglot environments where Java/.NET is just one of several language integration needs.

Other Alternatives Worth Knowing

REST APIs

Wrap Java code in a web service and call from .NET via HTTP. Adds 5–50ms latency per call but is language-independent and well-understood. Best for distributed systems where Java and .NET already run on different machines.

gRPC

High-performance RPC with Protobuf serialization. 1–10ms latency with strong typing through .proto files. Best for high-throughput service-to-service communication.

GraalVM Native Image

Compile Java to a native shared library, load from .NET via P/Invoke. Experimental, with severe restrictions on reflection and dynamic class loading.

JNI + P/Invoke (Manual Bridge)

Maximum control but enormous development effort. Only realistic for very narrow integration surfaces with deep C++ expertise.

Performance Benchmarks

Metric JNBridgePro IKVM Javonet REST gRPC
Single call latency 1–50µs 0 (native) Low µs 5–50ms 1–10ms
Throughput (calls/sec) 100K+ Native .NET High 1–10K 10–50K
Memory overhead JVM heap (256MB–1GB) Minimal Runtime overhead Separate process Separate process
Startup time JVM init (1–3s) None Runtime init Service startup Service startup

Key insight: IKVM wins on raw per-call performance because there’s no cross-runtime boundary. But IKVM’s Java 8 limitation means you’re trading performance for compatibility. For most enterprise workloads, JNBridgePro’s microsecond overhead is negligible compared to the business logic and I/O surrounding each call.

Decision Framework

Choose JNBridgePro if:

  • You use Java 9+ (Java 11, 17, 21)
  • You need professional support with SLAs
  • Your Java code uses dynamic class loading, reflection, or complex frameworks
  • You’re deploying in Docker/Kubernetes
  • Long-term reliability and vendor stability matter

Choose IKVM if:

  • Your Java code targets Java SE 8 exclusively
  • The library is simple and self-contained
  • You need zero cross-runtime overhead
  • Open-source licensing is required

Choose Javonet if:

  • You need to integrate more than just Java and .NET
  • The invoke-based API works for your use case
  • Subscription pricing fits your budget

Choose REST/gRPC if:

  • Java and .NET run on different machines
  • Call frequency is low (< 100 calls/sec)
  • You want complete language independence

FAQ

Which Java/.NET bridge is the most popular?
JNBridgePro has the longest track record (since 2001) and the largest enterprise user base. IKVM has the most open-source downloads but limited active usage due to its Java 8 restriction.

Can I use IKVM and JNBridgePro together?
Technically yes, but not recommended. Pick the approach that best fits your Java version and requirements.

Is there a free Java/.NET bridge for production?
IKVM is free (MIT), but limited to Java SE 8. JNBridgePro offers a free evaluation — production requires a commercial license.

How long does integration take?
JNBridgePro: typically 1–2 days including proxy generation. IKVM: hours for simple JARs, but translation issues can take weeks. Javonet: 1–2 days with more verbose code.

Migrating from IKVM?
See the IKVM to JNBridgePro migration guide. Typical migration: 2–4 weeks for medium-sized applications.

Originally published at jnbridge.com.

I Built an AI That Matches Lonely People with Therapy Pets — Here’s What I Learned

Ever had a bad week and thought, “I just need to hug a dog right now”?

You’re not alone. Science agrees. And I built something about it.

This post is about MyPetTherapist — an AI-powered platform that matches people with certified therapy animals — and all the weird technical decisions I made along the way.

The Problem Worth Solving

Therapy animals work. Studies show pet therapy reduces cortisol by up to 37%, lowers blood pressure, and reduces anxiety faster than most medications. But most people have no idea:

  • Which animal type actually fits their emotional need
  • Whether their living situation (apartment, allergy-prone, busy schedule) is compatible
  • How to find certified therapy animals vs. just… someone’s emotional support peacock

Enter: AI.

The Stack (The Honest Version)

I’m going to be real — the first version was a mess.

# Version 1: The Embarrassing Version
def match_pet(user_input):
    if "sad" in user_input:
        return "dog"
    if "stressed" in user_input:
        return "cat"
    return "fish"  # the "I give up" option

Yeah. A dictionary pretending to be AI.

Version 2 was better:

# Version 2: Actual Semantic Matching
from sentence_transformers import SentenceTransformer
import numpy as np

model = SentenceTransformer('all-MiniLM-L6-v2')

def embed_profile(text: str) -> np.ndarray:
    return model.encode(text, normalize_embeddings=True)

def match_therapy_animal(user_profile: str, animal_profiles: list[dict]) -> dict:
    user_vec = embed_profile(user_profile)

    scores = []
    for animal in animal_profiles:
        animal_vec = embed_profile(animal['description'])
        similarity = float(np.dot(user_vec, animal_vec))
        scores.append((similarity, animal))

    scores.sort(key=lambda x: x[0], reverse=True)
    return scores[0][1]  # Best match

This was 10x better. But still missing the nuance of why someone needs therapy support.

The Breakthrough: Structured Emotional Intake

The real game-changer was treating the matching problem like a multi-dimensional vector search problem, not a keyword problem.

Users answer 5 questions. We don’t analyze the words — we analyze the emotional signature:

class EmotionalProfile:
    dimensions = [
        'anxiety_level',      # 0-10
        'social_energy',      # introvert <-> extrovert
        'physical_activity',  # couch potato <-> hiker
        'commitment_capacity',# time/energy available
        'touch_sensitivity'   # some people hate fur, love scales
    ]

    def score(self, responses: dict) -> np.ndarray:
        # NLP scoring via fine-tuned classifier
        return np.array([self._score_dimension(d, responses) 
                         for d in self.dimensions])

Each therapy animal type also gets scored on these same 5 dimensions. The match is a cosine similarity in emotional space.

What Actually Surprised Me

Rabbits outperform dogs for anxiety. High-anxiety users matched better with rabbits — quieter, don’t demand attention, warm and furry. This never would have come from keyword matching.

Fish are underrated for insomnia. The visual rhythm of a fish tank is legitimately studied as a sleep aid. Users who reported sleep issues matched fish tanks 40% of the time.

The intake form itself was therapeutic. Multiple users said just answering the questions helped them understand what they were feeling. The AI wasn’t doing the heavy lifting — the reflection was.

The Infrastructure (Keep It Simple, Stupid)

I deliberately kept the stack boring:

  • Backend: Python + FastAPI
  • Embeddings: sentence-transformers (local, free, fast enough)
  • Database: PostgreSQL with pgvector extension for similarity search
  • Frontend: WordPress (I know. I know. It works.)
  • Hosting: Single VPS, no Kubernetes drama
-- pgvector similarity search
SELECT 
  animal_id,
  name,
  species,
  1 - (embedding <=> $1) AS similarity_score
FROM therapy_animals
WHERE is_certified = true
ORDER BY embedding <=> $1
LIMIT 5;

The query that powers the entire matching engine is 8 lines of SQL. No LangChain. No vector database SaaS. Just PostgreSQL doing what it’s always done.

Lessons for Anyone Building Wellness AI

1. Start with the emotion, not the solution. Most health-tech apps ask “what do you want?” — that’s the wrong question. Ask “how do you feel?” and let the AI figure out the solution.

2. Interpretability matters more than accuracy. If a user gets matched with a rabbit and you can’t explain why, they won’t trust it. Show your work.

3. The AI is the last 20%. The first 80% is the intake design, the database of certified providers, the trust-building UX. AI is the finishing touch, not the foundation.

4. Boring infrastructure scales. I see people reaching for Pinecone on day 1. Start with pgvector. You’ll save $300/month and learn more.

What’s Next

Currently working on:

  • Live session matching — not just animal type, but specific available sessions near you
  • Outcome tracking — did the match actually help? 30-day check-in flows
  • Provider onboarding API — so certified therapy animal organizations can self-register

If you’re curious about the project: mypettherapist.com — it’s live, it’s free to try the matching quiz, and yes, you might get matched with a guinea pig. Lean into it.

What’s the weirdest “AI for wellness” use case you’ve seen? Drop it in the comments — genuinely collecting data points here.

OpenClaw vs K8s Agent Orchestrators (2026)

Optio just dropped on Hacker News promising ticket-to-PR automation via Kubernetes pods, and the comments section predictably split between enterprise architects drooling over YAML configs and solo devs asking “cool, but do I really need a K8s cluster to run an AI agent?” The answer, for the vast majority of developers looking at AI agents for automation in 2026, is a resounding no.

Claude’s Code live feed tells the real story — the overwhelming majority of AI-generated commits land in repos with fewer than two stars. These aren’t Fortune 500 engineering teams. They’re individual developers, small teams, and weekend hackers who need agent orchestration that works on a single machine without a DevOps degree.

The New Wave of Agent Orchestrators

We’re at an inflection point. The first generation of AI coding agents — Copilot, Cursor, Aider — were single-threaded tools. You asked, they answered, you copy-pasted. The second generation brought autonomy: agents that could read your codebase, run tests, and push commits. Now we’re entering the third wave: multi-agent orchestration, where specialized agents collaborate on complex tasks.

The problem is that “orchestration” means wildly different things depending on who’s selling it.

On one end, you’ve got Kubernetes-native platforms like Optio that treat each agent task as an ephemeral pod. On the other end, you’ve got OpenClaw’s sub-agent system, which spawns lightweight background agent runs within a single Gateway process. No containers. No YAML. No cluster.

Optio’s K8s Model: What It Gets Right

Credit where it’s due. Optio’s architecture is genuinely clever for its target market:

  • Hard isolation — Each agent runs in its own container. Zero chance of cross-task contamination.
  • Scalability ceiling — K8s autoscaling means you can theoretically run hundreds of agent tasks simultaneously.
  • Environment parity — Each pod can mirror your CI environment exactly.

If you’re a platform team at a company with 50+ developers and existing K8s infrastructure, this model makes sense.

The Problem: You Probably Don’t Have a Kubernetes Cluster

Let’s talk about the elephant in the room: the actual demographic of people using AI coding agents.

For most developers, a Kubernetes-based agent orchestrator introduces:

  • Infrastructure overhead — Minimum viable K8s for agent workloads runs $150-300/month before API calls
  • Operational complexity — Pod scheduling, resource limits, RBAC, network policies… you’re adopting an entire operational paradigm
  • Cold start latency — 30 seconds to several minutes per task
  • Cost scaling — Container orchestration costs on top of LLM API costs

Most people need something that works on their laptop.

OpenClaw’s Sub-Agent Architecture

OpenClaw takes the opposite approach. Instead of treating each agent task as an infrastructure problem, it treats it as a session management problem.

OpenClaw runs as a single Gateway process on whatever machine you’ve got — a MacBook, a Raspberry Pi, a $5 VPS. When you need multiple agents working in parallel, you spawn sub-agents: background agent runs in isolated sessions within the same Gateway.

Each sub-agent gets:

  • Its own isolated session and conversation context
  • Its own tool access, scoped appropriately
  • Automatic result delivery back to the parent
  • Configurable model and thinking level

The key insight: session isolation is sufficient for most agent orchestration tasks. You don’t need container isolation when your agents are reading files, making API calls, and writing code.

Head-to-Head Comparison

Capability K8s Orchestrators OpenClaw Sub-Agents
Setup time Hours to days Minutes (npm install)
Minimum infra K8s cluster Any machine with Node.js
Monthly infra cost $150-300+ LLM API only
Task isolation Container-level Session-level
Cold start 30s-3min Near-instant
Error recovery Pod restart Session retry/steer
Target user Platform teams Solo devs, small teams

When to Use Which

Choose K8s orchestrators if:

  • You already have a managed K8s cluster
  • You need hard container isolation for compliance
  • You’re processing large batch workloads overnight

Choose OpenClaw sub-agents if:

  • You’re a solo developer or small team
  • You want agent orchestration today, not after infrastructure setup
  • You need fast feedback loops
  • You’re running on a single machine
  • You want to control costs

The Bottom Line

The AI agent orchestration space is splitting into two lanes: infrastructure-heavy platforms that assume you’re running Kubernetes, and lightweight systems that meet developers where they actually are.

For the vast majority of developers exploring AI agents in 2026, OpenClaw is the right starting point. You can always scale to K8s later — but most people never will.

The future of AI agent orchestration isn’t about who can spin up the most containers. It’s about who can get the most useful work done with the least overhead.

Originally published on TechPulse Daily.

7 Mac Apps for Developers Preparing for Technical Interviews in 2026

Whether you’re grinding LeetCode, rehearsing system design, or just trying to stay sharp between rounds, your environment matters. The right tools can cut friction so you focus on what counts: nailing the interview.

Here are 7 Mac apps that helped me (and developers I know) get through interview prep without losing our minds.

1. Raycast — Your Command Center

Raycast replaces Spotlight with something actually useful for developers. During interview prep, I used it constantly — quick calculator for Big-O comparisons, clipboard history for pasting code snippets between windows, and instant window tiling so I could have my IDE and a problem statement side by side. The snippet expansion feature is great for saving common algorithm templates.

Price: Free (Pro $8/mo)

2. Obsidian — Your Second Brain for Patterns

Obsidian is a local-first markdown note-taking app with bidirectional linking. I built an entire graph of algorithm patterns — linked “sliding window” to “two pointers” to “hash map tricks” — and reviewing it before interviews was like having a cheat sheet that actually taught me something. The search is lightning fast and everything stays on your machine.

Price: Free for personal use

3. Monk Mode — Kill the Feeds, Keep the Focus

Monk Mode blocks distracting feeds at the content level without nuking entire apps. During interview prep, this was essential. I could still use Twitter to DM recruiters but the infinite feed was gone. Same with YouTube — tutorial videos worked fine, but the homepage couldn’t pull me into a 2-hour rabbit hole. When you’re spending 3-4 hours a day on problems, protecting that focus time is non-negotiable.

Price: $15 lifetime

4. Warp — A Terminal That Doesn’t Fight You

Warp is a Rust-based terminal with modern editing, command search, and AI assistance built in. For interview prep, the block-based output is a game changer — you can copy just the output of a specific command without wrestling with text selection. The built-in AI can explain error messages when you’re testing solutions locally, which saves trips to Stack Overflow.

Price: Free (Teams plan available)

5. TokenBar — Track What Your AI Study Sessions Cost

TokenBar sits in your menu bar and shows real-time token usage across LLM APIs. If you’re using ChatGPT, Claude, or Copilot to help explain problems, verify solutions, or do mock interviews, costs add up fast. TokenBar helped me realize I was burning $4/day on GPT-4 during prep. I adjusted my workflow — used cheaper models for simple explanations and saved the heavy models for complex system design questions.

Price: $5 lifetime

6. Rectangle — Snap Your Windows Into Study Mode

Rectangle is a free, open-source window manager. Interview prep means juggling a lot of windows: LeetCode on the left, your IDE on the right, maybe notes on a second monitor. Rectangle’s keyboard shortcuts make this instant. I set up a muscle-memory layout — problem on the left third, code in the middle third, terminal on the right — and it cut my context-switching time dramatically.

Price: Free and open source

7. Numi — Think Through Problems Without Leaving Your Keyboard

Numi is a text-based calculator that feels like a scratchpad. During interview prep, I used it constantly for back-of-the-envelope calculations: “if this array has 10^6 elements and my solution is O(n log n), that’s roughly 20 million operations” — just type it out in plain English mixed with math. It’s way faster than opening a Python REPL for quick arithmetic, and the natural language support means you don’t break your flow.

Price: Free

Wrapping Up

Interview prep is a grind, but it doesn’t have to be a disorganized one. These tools won’t teach you dynamic programming, but they’ll make sure your environment isn’t working against you while you learn it.

Got a tool that helped you through interview prep? Drop it in the comments — always looking for new additions to the toolkit.

Happy interviewing, and may your Big-O always be optimal. 🎯

Türkiye imzalı uzay hackathonu dünyaya yayılacak

Türkiye Uzay Ajansı öncülüğünde düzenlenen ‘TUA Astro Hackathon’ ile gençlerin uzay alanındaki farkındalığı artırılacak. Etkinlik, önümüzdeki yıllarda 78 ülkeye yayılması planlanıyor ve çeşitli uzay problemlerine çözümler arayan takımların katılımıyla gerçekleştiriliyor.