Sergey Korobkov

Sergey Korobkov

Web designer & developer

0%
Kursi

Currency rates and transfer routes, ranked by what actually arrives.

Kursi cover
ServiceProductDesignDevelopment
ClientIndependent founder
Tools usedNext.jsReactTypeScriptTailwindPlaywright
Year2026
Overview

A comparison tool built around the only number that matters.

Georgia became home to a large wave of Russian-speaking newcomers, and with them came a daily question: what's the cheapest way to move money into the country. Dozens of routes exist — banks, cash systems, SWIFT, crypto — each with its own rate, fee, and delivery time. The information was scattered across bank websites, Telegram channels, and forum threads. A client came to me with the idea and a rough scope: build a service that puts all of it in one place. What it should actually rank by, how the data would be collected, and what the interface needed to look like were open questions. I took the project end to end — product decisions, interface, and code.

Challenge

The obvious metric was the wrong one.

The initial brief assumed the standard approach: list the routes, show the commission, let people compare. A few comparison sites already worked that way. All of them shared the same flaw — ranking by declared commission is misleading. A transfer advertised at 0% with a poor exchange rate delivers less than one charging 2% at a fair rate. The margin usually hides in the rate, not the fee, which meant those sites were consistently pointing people toward worse options. I proposed changing the core ranking logic before design started. The client agreed, and it became the defining decision of the product. The second problem was trust. Rates go stale within hours. A site that displays yesterday's number without saying so isn't neutral — it's actively misleading someone about to move real money.

Approach

Rank by outcome, not by advertised terms.

The converter calculates the final amount received and sorts by that. It reorders results substantially compared to fee-based ranking — and it's the only figure that affects the person sending money. Three principles shaped everything after that:

Verified and estimated data are labeled separately.

Some routes were tested directly, with a known exact figure. Others are calculated from the current National Bank rate. Those are different levels of confidence, and collapsing them into one number would undermine the whole product.

A missing row beats a stale one.

If a source fails during collection, its row simply doesn't render. Someone planning a transfer around an outdated rate is worse off than someone who sees one fewer option.

Route type is a filter, not a footnote.

Bank-to-bank, cash pickup, and crypto serve different situations. Someone who needs money within the hour and someone optimising for rate are solving different problems.

Research

Understanding how people actually choose a transfer route.

Research came from two directions: existing comparison tools, and the informal channels people were already using — expat Telegram groups, forum threads, and word-of-mouth recommendations.

Competitor analysis

Existing services fell into two categories. Aggregators covered many routes but ranked them by fee, which produced misleading order. Bank-specific pages were accurate but showed only one option, forcing manual comparison across a dozen tabs. Neither handled the Georgia-specific routes people were actually using — cash systems, P2P crypto, and the particular bank pairings that work between Russia and Georgia.

Visual direction

The reference points weren't other comparison sites. They were financial dashboards and data tools — interfaces where dense information stays scannable. Tabular layout, restrained colour, typography doing the structural work, and no decorative elements competing with the numbers.

Design Solutions

Every element earns its place in a table this dense.

The interface carries a lot of information: 17 routes, 7 currencies, multiple rate types, delivery times, and confidence labels. The design work was almost entirely about hierarchy — making that volume readable at a glance.

Typography

Type does the structural work. A tight scale separates route names, amounts, and metadata without relying on colour or dividers. Tabular figures keep numbers aligned across rows, so amounts are comparable vertically without scanning.

Layout system

The comparison table is the product; everything else supports it. Filters sit directly above the results and update them without a page change. Secondary information — delivery time, method type, source — is present but visually recessed so the final amount stays dominant.

Components

A component layer built on Radix primitives: buttons, cards, badges, filter controls, and the table itself. Radix handles accessible behaviour for select, dropdown, tabs, and tooltip; the visual system on top is custom. Components were built to stay coherent as routes and currencies get added.

Data confidence badges

Verified rates and estimates carry distinct badges. Small detail, but it's the visual expression of the product's core principle — and the first thing that separates it from competitors when you look closely.

Engineering

Georgian banks don't publish an API.

Collecting the data turned out to be the hardest part of the project. Official rates come from the National Bank of Georgia and the Central Bank of Russia through XML and JSON endpoints — straightforward. Commercial banks were not. Several render their rates client-side, so a plain fetch returns an empty page. The collection script runs two tracks: Playwright drives a real headless browser for JavaScript-rendered sites, Cheerio parses HTML for static ones. P2P exchange rates come from a separate source for USDT. Everything writes to a single snapshot in public/data/rates.json, including per-source status — ok, failed, or skipped, with a timestamp. That status log is what makes the "hide stale data" rule enforceable rather than aspirational.

Architecture

Next.js 15 with React 19 and TypeScript, fully statically generated. There's no backend — the data layer is a JSON file refreshed by the collection script. For a site where rates update a few times daily, running a server would have added cost and failure modes for no benefit. Localisation runs through a custom React context rather than an i18n library. Standard packages assume URL-based routing (/en/...); the requirement here was a client-side toggle that keeps the user on the same page and remembers the choice. Writing it directly took less time than configuring around a library built for something else.

SEO structure

Static pages generate for every currency pair — /currency/usd-gel, /currency/rub-eur, and so on — cross-linked by common transfer amounts. Specific searches land on specific pages rather than a generic homepage.

Outcome

17 routes, 7 currencies, one honest number.

The finished product covers bank transfers (Sberbank to Credo, T-Bank and Ozon to TBC and BOG), SWIFT through OTP, UniCredit, BCS and Gazprombank, cash systems including Contact, Unistream, Zolotaya Korona, Avosend, KWIKPAY, Unired and Russian Post, and USDT through P2P exchanges. Rates cover USD, EUR, GEL, RUB, GBP, TRY and USDT across cash, card, official and P2P pricing. The interface runs in Russian and English.

What the project taught me

The most valuable contribution wasn't technical. It was pushing back on the original ranking logic before any design work started. The obvious number was the wrong one, and rebuilding around the right one is what made the product different from what already existed. The second was accepting incomplete data. Showing fewer routes honestly is a better product than showing all of them with numbers that might be wrong.